《JavaSE-第八章》-代码块与内部类

《JavaSE-第八章》-代码块与内部类,第1张

前言

本章介绍static成员,使用代码块初始化对象以及内部类。

博客主页:KC老衲爱尼姑的博客主页

博主的github,平常所写代码皆在于此

共勉:talk is cheap, show me the code

作者是爪哇岛的新手,水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!


文章目录
    • ✈1.static成员
      • ✈1.1static修饰成员变量
      • ✈1.2static修饰成员方法
      • ✈1.3static成员内存解析图
      • ✈1.4static成员总结
    • ✈2.代码块
      • ✈2.1普通代码块
      • ✈2.2构造代码块
      • ✈2.3静态代码块
      • ✈2.4初始化顺序总结
    • ✈3.内部类
      • ✈3.1实例内部类
      • ✈3.2静态内部类
      • ✈3.3局部内部类
      • ✈3.4匿名内部类

✈1.static成员

看过《射雕英雄传》的朋友都知道,陈玄风,陆乘风,梅超风都是桃花岛主黄药师的学生。那么我们如果想通过学生类去出创造出这三个人,我们是否需要将学生类中添加一个成员变量来记录他们的老师呢?答案是不用的。因为在类中成员变量,每个对象都会包含,成员变量是用来描述具体学生的信息。而在江湖中人人都知道他们的老师是黄老师,没必要自己在举个写着我的老师是黄药师的牌子告诉别人。既然这份信息是共享的,那么我们不需要所有的对象都储存一份信息,只需要将这份信息共享给所有的对象即可。在java中要实现共享可以使用static去修饰成员,被static修饰的成员,称之为静态成员,也被称为静态类,不属于具体的对象,属于类,是所有的对象所共享的。

✈1.1static修饰成员变量

桃花岛学生类


package Demo;

public class PeachBlossomIslandStudent {
   private String name;
   private String sex;
   private double height;
  public static final String  teacher="黄药师";
    public PeachBlossomIslandStudent() {
    }


    public PeachBlossomIslandStudent(String name, String sex, double height) {
        this.name = name;
        this.sex = sex;
        this.height = height;

    }


    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", height=" + height +
                ",teacher="+ PeachBlossomIslandStudent.teacher+
                '}';
    }
}

学生测试类

package Demo;


public class PeachBlossomIslandStudentText {
    public static void main(String[] args) {
        PeachBlossomIslandStudent s=new PeachBlossomIslandStudent("陈玄风","男",1.7);
        PeachBlossomIslandStudent s2=new PeachBlossomIslandStudent("梅超风","女",1.70);
        PeachBlossomIslandStudent s3=new PeachBlossomIslandStudent("陆乘风","男",1.70);
        System.out.println(s);
        System.out.println(s2);
        System.out.println(s3);
    }
}

因为被static修饰的成员变量是共享的,同时也是可以修改的,但是上述陈玄风,梅超风,陆乘风一辈子只有一个老师,所以我们可以用final再次修饰静态成员,final意为最终的,是不可变的,它修饰的变量是能赋值一次。

✈1.2static修饰成员方法

sattic修饰的方法被成员静态方法

public class PeachBlossomIslandStudent {
   private String name;
   private String sex;
   private double height;
  public static final String  teacher="黄药师";

    public PeachBlossomIslandStudent() {
    }


    public PeachBlossomIslandStudent(String name, String sex, double height) {
        this.name = name;
        this.sex = sex;
        this.height = height;

    }



    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", height=" + height +
                ",teacher="+ PeachBlossomIslandStudent.teacher+
                '}';
    }
    //静态方法
    public static void flute(){
        System.out.println("碧海潮生曲");
    }

    public static void sword(){
        System.out.println("落英神剑掌");
    }
    //成员方法
    public void study(){
        System.out.println("好好学习");

    }

}

现在知道了如何使用static,那么如何访问被static修饰的成员呢?在java中被static修饰的成员是属于类的,而不是属于对象独有,所以我们一般是用类名.属性或者类型.方法名,来使用被static修饰的成员

使用static修饰的成员

代码示例

package Demo;

/**
 * truth:talk is cheap, show me the code
 *
 * @author KC萧寒
 * @description
 * @createDate: 2022-05-14 09:34
 */

public class PeachBlossomIslandStudentText {
    public static void main(String[] args) {
        PeachBlossomIslandStudent s=new PeachBlossomIslandStudent("陈玄风","男",1.7);
        PeachBlossomIslandStudent s2=new PeachBlossomIslandStudent("梅超风","女",1.70);
        PeachBlossomIslandStudent s3=new PeachBlossomIslandStudent("陆乘风","男",1.70);
        System.out.println(s);
        System.out.println(s2);
        System.out.println(s3);
        PeachBlossomIslandStudent.sword();
        PeachBlossomIslandStudent.flute();
        s.sword();//错误使用
        s.flute();//错误使用
    }
}

切记被static修饰的成员本质上属于类,但是它又是被所有对象所共享,所以可以通过对象的引用去访问。这种方式的访问是不建议使用,我们通常访问静态成员使用类名去访问。

静态方法中无法使用实例成员变量

静态方法

成员方法

对于静态的方法它属于类,不属于特定对象,所以它的形参列表中默认是没有this,没有this自然就不能使用成员变,而成员方法中的形参列表默认有this,这样我们才能调用当前类的成员。

✈1.3static成员内存解析图

解析此代码

package Demo;


public class PeachBlossomIslandStudent {
   private String name;
   private String sex;
   private double height;
  public static final String  teacher="黄药师";

    //静态方法
    public static void flute(){
        System.out.println("碧海潮生曲");
    }

    public static void sword(){
        System.out.println("落英神剑掌");
    }
    //成员方法
    public void study(){
        System.out.println("好好学习");

    }

    public static void main(String[] args) {
        PeachBlossomIslandStudent s=new PeachBlossomIslandStudent();
        s.name="陈玄风";
        s.sex="男";
        s.height=1.90;
        PeachBlossomIslandStudent s2=new PeachBlossomIslandStudent();
        s2.name="梅超风";
        s2.sex="女";
        s2.height=1.70;
    }
}

内存图

✈1.4static成员总结

成员变量特性

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
  2. 类变量存储在方法区当中
  3. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
  4. 可以通过对象的引用访问,也可以通过类名访问,建议使用类名访问

成员方法特性

  1. 是类的方法,不属于对象。
  2. 不能在静态方法中访问任何非静态成员变量
  3. 可以通过对象调用,也可以通过类名.

static成员变量初始化

注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性

静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。

  1. 就地初始化 就地初始化指的是:在定义时直接给出初始值
 public static final String  teacher="黄药师";//就地初始化

接下来就来介绍代码块

✈2.代码块

代码块概念以及分类

使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:

  1. 普通代码块
  2. 构造块
  3. 静态块
  4. 同步代码块(暂时不介绍)
✈2.1普通代码块

普通代码块:定义在方法中的代码块.

代码示例

public class Demo {
    public static void main(String[] args) {
        {
            int age=99;
            System.out.println(age);
        }
        age=9;
    }
}

代码块中的age是局部变量,大括号是它的作用域即使用范围,所以代码外的age是无法使用的。这个普通代码块基本不用。

✈2.2构造代码块

构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。

代码示例

public class Demo {
    private int age;
    private String name;
    private String school;

    public Demo() {
        System.out.println("无参构造器初始化");
    }

    {
        System.out.println("构造块初始化");
        age = 12;
        name = "小白";
        school = "家里蹲";
    }

    public void show() {
        System.out.println("name: " + name + " age: " + age + " schoo: " + school);
    }

    public static void main(String[] args) {
        Demo d = new Demo();
        d.show();
    }
}

运行结果

构造块初始化
无参初始化
name: 小白 age: 12 schoo: 家里蹲

结论:构造块的初始化早于构造器的。

✈2.3静态代码块

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。

代码示例

public class Demo {
    private   int age;
    private   String name;
    private static  String school;

    public Demo() {
        System.out.println("无参构造器初始化");
    }

   static {
        System.out.println("静态代码块初始化");
        school = "家里蹲";
    }

    {
        System.out.println("实例代码块初始化");
        age = 12;
        name = "小白";
    }

    public void show() {
        System.out.println("name: " + name + " age: " + age + " schoo: " + school);
    }

    public static void main(String[] args) {
        Demo d = new Demo();
        d.show();
    }
}

运行结果

静态代码块初始化
实例代码块初始化
无参构造器初始化
name: 小白 age: 12 schoo: 家里蹲
✈2.4初始化顺序总结
package task;


public class LifeCycle {
    //静态属性
    private static String staticField = getStaticField();

    //静态代码块
    static {
        System.out.println(staticField);
        System.out.println("静态代码初始化");
    }

    //普通属性/成员属性
    private String field = getField();

    //普通方法块/实例代码块
    {
        System.out.println(field);
        System.out.println("实例代码块初始化");
    }

    //构造函数初始化
    public LifeCycle() {
        System.out.println("构造函数初始化");
    }

    public static String getStaticField() {
        String statiFiled = "静态属性初始化";
        return statiFiled;
    }

    public static String getField() {
        String filed = "普通属性初始化";
        return filed;
    }
    
    public static void main(String[] argc) {
        new LifeCycle();
    }
}

运行结果

静态属性初始化
静态代码初始化
普通属性初始化
实例代码块初始化
构造函数初始化

初始化顺序优先级:静态属性>经静态代码块>实例属性>实例代码块>构造器

注意事项

  1. 静态代码块不管生成多少个对象,其只会执行一次
  2. 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
  3. 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并
  4. 实例代码块只有在创建对象时才会执行
✈3.内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服 务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

内部类好比是连接佛珠的线将一颗一颗珠子连接在一起

代码示例

public class Outer {

    public class Inner{

    }

    public static void main(String[] args) {
        new Outer();
    }
}

//错误认知
public class A{
    
}

class B{
}
//A B是两个独立的类

注意事项

  1. 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类
  2. 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件

内部类的分类根据内部类定义的位置不同,一般可以分为以下几种形式:

  1. 成员内部类(普通内部类:未static修饰的成员内部类 和 静态内部类:被static修饰的成员内部类)(在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类。)
  2. 局部内部类(不谈修饰符)、匿名内部类
✈3.1实例内部类

即未被static修饰的成员内部类。

内部类中能不能定义静态成员?

通过代码来验证

public class Outer {

    public class Inner{
    static int age=90;
    }

    public static void main(String[] args) {
        new Outer();
    }
}

运行结果

java: 内部类Demo.Outer.Inner中的静态声明非法
  修饰符 'static' 仅允许在常量变量声明中使用

此时看来是不能的,当给statc修饰的成员变量加上final就可以使用了

public class Outer {

    public class Inner{
    static final  int age=90;
    }

    public static void main(String[] args) {
        new Outer();
    }
}

结论:内部类中要定义静态成员必须使用static final修饰

实例内部类中能不能定义静态成员方法?

代码示例

public class Outer {

    public static void study(){
        System.out.println("好好学习");
    }

    public class Inner{
    static final  int age=90;
    }

    public static void main(String[] args) {
        new Outer();
    }
}

答案是可以

成员内部类创建对象的格式

格式:外部类名.内部类名 对象名 = new  外部类构造器.new 内部类构造器();
范例:Outer.Inner in =  new Outer().new  Inner();

实例内部类代码大全示例

package task;


public class Outer {
    private static int a;
    static int b;
    int c;

    public void methodA() {
        Outer.Inner in=new Outer().new Inner();
        in.name="hmr";
        System.out.println(in.name);
        a = 99;
        System.out.println(a);
    }

    public static void methoB() {
        Outer.Inner in=new Outer().new Inner();
        in.name="叶子秋";
        System.out.println(in.name);
        b = 100;
        System.out.println(b);
    }
	// 实例内部类:未被static修饰
    public class Inner {
        int c;
        static final int age=19;
        String name;
        public void mathodC() {
            // 在实例内部类中可以直接访问外部类中:任意访问限定符修饰的成员
            a = 90;
            b = 100;
            methodA();
            methoB();
            // 如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的
            c = 900;
            System.out.println(c);
            // 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
            System.out.println(Outer.this.c);
        }
    }
}

测试类

package task;


public class OuterText {

    public static void main(String[] args) {
        // 要访问实例内部类中成员,必须要创建实例内部类的对象
		// 而普通内部类定义与外部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类
		// 外部类:对象创建 以及 成员访问
        Outer o=new Outer();
        // 创建实例内部类对象,要得到内部类先要创建外部类
        //Outer.Innner in=new Outer.new Inner(); 方式一
        
        Outer.Inner in=o.new Inner();//方式二
        
        in.mathodC();
    }
}

注意事项

  1. 外部类中的任何成员都可以被在实例内部类方法中直接访问
  2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
  3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名 称.this.同名成员 来访问
  4. 实例内部类对象必须在先有外部类对象前提下才能创建
  5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
  6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。

成员内部类的访问

1、成员内部类中是否可以直接访问外部类的静态成员?

可以,外部类的静态成员只有一份可以被共享访问。

2、成员内部类的实例方法中是否可以直接访问外部类的实例成员?

可以的,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。

✈3.2静态内部类

什么是静态内部类?

  • 有static修饰,属于外部类本身。

  • 它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已。

静态内部类创建对象的格式:

格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器;

代码示例

package task;


public class OuterStatic {
    private int a;
    static int b;

    public void methodA(){
        a=90;
        System.out.println(a);
    }
    public static void methodB(){
        b=100;
        System.out.println(b);
    }

    public static class InnerStatic{

        public void method(){
           // a=90;
            b=78;
            methodB();
           // methodA();
        }

    }

}

静态内部类注意事项

  1. 在静态内部类中只能访问外部类中的静态成员
  2. 创建静态内部类对象时,不需要先创建外部类对象
✈3.3局部内部类

局部内部类放在方法、代码块、构造器等执行体中

代码示例

package Demo;

public class Outer {
    int a = 90;
    public void study() {
        int b = 99;
        // 局部内部类:定义在方法体内部
		// 不能被public、static等访问限定符修饰
        class Inner {
            public void print() {
                System.out.println(a);
                System.out.println(b);
            }
        }
        // 只能在该方法体内部使用,其他位置都不能用
        Inner in = new Inner();
        in.print();
    }
    
}

局部内部类的类文件名为: 外部类$N内部类.class

局部内部类了解即可

✈3.4匿名内部类

本质上是一个没有名字的局部内部类,定义在方法中、代码块中、等

作用:简化代码

特点总结:

  • 匿名内部类是一个没有名字的内部类。

  • 匿名内部类写出来就会产生一个匿名内部类的对象。

  • 匿名内部类的对象类型相当于是当前new的那个的类型的子类类型。

代码一

public class Text {
    public static void main(String[] args) {
        Animal a=new Tiger();
        a.run();
    }
}

class Tiger extends Animal {
    @Override
    public void run() {
        System.out.println("老虎跑的快!");
    }
}

abstract class Animal {
    public abstract void run();
}

通过匿名内部类简化代码

public class Text {
    public static void main(String[] args) {
        Animal a = new Animal() {
            @Override
            public void run() {
                System.out.println("老虎跑的快!");
            }
        };
        a.run();
    }
}

abstract class Animal {
    public abstract void run();
}

使用匿名内部类实现老师,学生同时打游戏

package inner;
//简化版一
public class Text2 {
    public static void main(String[] args) {
        IPlayGame ip = new IPlayGame() {
            @Override
            public void PlayGame() {
                System.out.println("学生打游戏!");
            }
        };
       playGame(ip);
        System.out.println("---------------");
        IPlayGame ip2 = new IPlayGame() {
            @Override
            public void PlayGame() {
                System.out.println("老师打游戏!");
            }
        };
        playGame(ip);
    }

    public static void  playGame(IPlayGame i){
        System.out.println("开始游戏");
        i.PlayGame();
        System.out.println("结束游戏");
    }

}

interface IPlayGame {
    void PlayGame();
}
//简化版二
//Lambda只能简化接口中只有一个抽象方法的匿名内部类形式(函数式接口) 

public class Text2 {
    public static void main(String[] args) {
        IPlayGame ip = () ->{
            System.out.println("学生打游戏!");
        };
       playGame(ip);
        System.out.println("---------------");
        IPlayGame ip2 = ()-> {
            System.out.println("老师打游戏!");
        };
        playGame(ip2);
    }

    public static void  playGame(IPlayGame i){
        System.out.println("开始游戏");
        i.PlayGame();
        System.out.println("结束游戏");
    }

}

interface IPlayGame {
    void PlayGame();
}



//简化版三
public class Text2 {
    public static void main(String[] args) {
       playGame(()->   System.out.println("学生打游戏!"));
        System.out.println("---------------");
        playGame(()->   System.out.println("老师打游戏!"));
    }

    public static void  playGame(IPlayGame i){
        System.out.println("开始游戏");
        i.PlayGame();
        System.out.println("结束游戏");
    }
}

interface IPlayGame {
    void PlayGame();
}

匿名内部类对象可以作为参数传递,因为它是当前new的那个类型的子类型,只要方法中的形参部分是它的父类型便可以接收。

上述代码调用过程图

最后的话

各位看官如果觉得文章写得不错,点赞评论关注走一波!谢谢啦!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/920930.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存