JavaSE-No.6.3——代码块与内部类

本文详细介绍了Java中的类和对象,包括代码块的执行顺序、内部类的四种类型及其特点。重点讲解了实例内部类和静态内部类的创建与访问规则,强调了内部类对外部类成员的访问限制,并通过实例展示了局部内部类和匿名内部类的用法。此外,还讨论了静态代码块和实例代码块的执行时机。
摘要由CSDN通过智能技术生成
JavaSE传送门

JavaSE_Start

JavaSE-No.6.1——Java的类和对象

JavaSE-No.6.2——封装与static



1. 代码块

使用 {} 定义的一段代码称为代码块

根据代码块定义的位置以及关键字,又可分为以下四种:

普通代码块(本地代码块)

定义在方法中的代码块称为普通代码块(不常用)

public void func1() {
    {
        System.out.println("本地代码块");
    }
}

构造块(实例代码块)

实例代码块定义在类的内部,方法的外部

构造代码块一般用于初始化实例成员变量

public class Test {
    {
        System.out.println("实例代码块");
    }
}

静态块

使用static定义的代码块称为静态代码块

静态代码块一般用于初始化静态成员变量。

static {
    System.out.println("静态代码块");
}

同步代码块(后续讲解多线程部分再谈)


1.1 代码块执行顺序

我们有这样一个代码

public class Test {
    public void func1() {
        {
            System.out.println("本地代码块");
        }
    }

    {
        System.out.println("实例代码块");
    }

    static {
        System.out.println("静态代码块");
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.func1();
    }
}

我们开始执行,输出结果会是什么?是只输出“本地代码块”吗?
在这里插入图片描述

不仅仅是输出了三行,顺序还和我们刚刚写的代码顺序不一样,这是为什么呢?

执行顺序

  1. 先执行静态的。[加载了类就会被执行]
  2. 如果有多个静态的,那么看定义顺序
  3. 如果没有实例化对象,那么只会执行静态的。[且静态的只会被执行一次]
  4. 实例的数据[有多个实例,要看定义的顺序]
  5. 执行构造函数

举例:(3)

我们在上面的代码的基础上,在main函数中增加两行代码

public static void main(String[] args) {
        Test test = new Test();
        test.func1();
        System.out.println("========");//新增加的
        Test test2 = new Test();
        test2.func1();
}

我们执行一下,可以看到此时静态的只被执行了一次。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wQMU9ifx-1652411447469)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]
# 注意事项 #

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

2. 内部类

可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

我们为什么需要内部类呢?

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

public class OutClass {//外部类
	class InnerClass{ //内部类
	
	} 
}

# 注意事项 #

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

2.1 内部类的分类

内部类一般分为以下四种:

  1. 实力内部类
  2. 静态内部类
  3. 本地内部类(了解)
  4. 匿名内部类(学完接口才能知道)

2.1.1 实例内部类

class OutClass {
    /**
     * 实例内部类
     */
    class InnerClass{

    }
}

我们加上几个成员和方法

class OutClass {

    public int date1 = 1;
    private int date2 = 2;
    public static int date3 = 3;

    class InnerClass{
        public int date4 = 4;
        private int date5 = 5;
        public static final int date6 = 6;
        public static int date7 = 7;//报错
        public InnerClass() {
            System.out.println("实例内部类的构造方法");
        }
        public void innerFunc() {
            System.out.println("实力内部类的普通方法");
        }
        public static void staticInnerFunc() {//报错

        }
    }
}

我们发现有内部类中两处他报错了。这说明#实例内部类中不能定义静态成员变量(如果定义,需用final修饰)和静态成员方法。#


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LkuoOem4-1652411447470)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]
我们将这两处删掉,我们来思考一个 问题 ,如何实例化内部类?

如果我们在mian函数中写InnerClass innerClass = new InnerClass();很显然是不对的

实例内部类是依赖于外部类对象的,所以InnerClass要被调用,必须先定义一个外部类的对象,通过外部类的对象来调用,那类型怎么拿到呢?用外部类类型.InnerClass

public class Test {
    public static void main(String[] args) {
        OutClass outClass = new OutClass();
        OutClass.InnerClass innerClass = outClass.new InnerClass();
        //也可以写成
        OutClass.InnerClass innerClass = new OutClass().new InnerClass();
    }
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6i0NMNM4-1652411447471)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]
我们现在在内部类InnerClass中加入一行代码,也定义一个date1,输出结果会是什么呢?

public int date1 = 0;


运行结果展示,我们可以看到,输出内容是0,为内部类中定义的date1。

那如果我们想要在内部类中访问外部类中的date1,应该怎么做呢?使用外部类类名.this.

public void innerFunc() {
        System.out.println("实例内部类的普通方法");
        System.out.println("内部类的date1:" + date1);
        System.out.println("外部类的date1:" + OutClass.this.date1);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DAfdziV0-1652411447472)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]

我们现在在内部类InnerClass中再加入一行代码,也定义一个静态date3。

public static final int date3 = 0;

我们要怎么拿到外部的date3呢?使用外部类类名.

public void innerFunc() {
        System.out.println("实例内部类的普通方法");
        System.out.println("内部类的date3:" + date3);
        System.out.println("外部类的date3:" + OutClass.date3);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ZvIt4fW-1652411447473)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]

# 注意事项 #

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

2.1.2 静态内部类

static修饰的内部成员类称为静态内部类。

class OutClass {
    /**
     * 静态内部类
     */
    static class InnerClass{
    
    }    
}

我们加上几个成员和方法

class OutClass {

    public int date1 = 1;
    private int date2 = 2;
    public static int date3 = 3;

    static class InnerClass {
        public int date4 = 4;
        private int date5 = 5;
        public static int date6 = 6;

        public InnerClass() {
            System.out.println("静态内部类的构造方法");
        }
        public void test() {
            System.out.println("执行test方法");
        }
    }
}

我们还是这样一个 问题 ,如何实例化内部类?

很显然如果我们在mian函数中写InnerClass innerClass = new InnerClass();依旧是不对的。

此时我们的内部类是静态的,是不依赖外部类对象的。拿到类型依旧是用外部类类型.InnerClass,等号右边要写成new 外部类类名.内部类构造函数

public static void main(String[] args) {
    OutClass.InnerClass innerClass = new OutClass.InnerClass();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tkcWTsot-1652411447474)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]

我们已知实例内部类可直接访问外部类的成员变量,那静态内部类是否可以呢?

我们看下图,可以看到直接访问date1date2报错了,而date3没有报错。因为 date1,date2是这个类的实例成员,静态内部类不依赖于外部类对象,也就是在使用静态内部类的时候,外部类对象都没有生成。所以#不能在静态内部类中,直接访问外部类非静态的数据成员。#

那如果我们想要在静态内部类中访问外部类非静态成员怎么办呢?

我们只需要在内部类中提供外部类对象就可以了。

方法1:

public void test() {
        System.out.println("执行test方法");
        OutClass outClass = new OutClass(); 
        System.out.println(outClass.date1);
        System.out.println(outClass.date2);
        System.out.println(date3);
}

方法2:

public OutClass outClass;
public InnerClass(OutClass outClass) {
        System.out.println("静态内部类的构造方法");
        this.outClass = outClass;
}
public void test() {
        System.out.println("执行test方法");
        System.out.println(outClass.date1);
        System.out.println(outClass.date2);
}

这时我们在实例化内部类时,应该给他传一个参数

public class Test {
    public static void main(String[] args) {
        OutClass.InnerClass innerClass = new OutClass.InnerClass(new OutClass());
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iIZopceU-1652411447474)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]

# 注意事项 #

  1. 在静态内部类中只能访问外部类中的静态成员,不可直接访问外部类中的非静态成员

  2. 创建静态内部类对象时,不需要先创建外部类对象


2.1.3 局部内部类

定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。

public static void func() {
    // 局部内部类:定义在方法体内部
    // 不能被public、static等访问限定符修饰
    class AA {
             
    }
}

我们也可以在局部内部类中定义方法。

public static void func() {

        class AA {
            public void test() {
                System.out.println("AA");
            }
        }
        // 只能在该方法体内部使用,其他位置都不能用
        AA aa = new AA();
        aa.test();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I5fcphcu-1652411447475)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]

# 注意事项 #

  1. 局部内部类只能在所定义的方法体内部使用

  2. 不能被publicstatic等修饰符修饰

  3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$内部类名字.class


2.1.4 匿名内部类

后序讲接口时会详细介绍。现在我们简单了解一下。

匿名对象

我们先写一个Person

class Preson {
    public String name;
    public int age;

    public void func() {
        System.out.println("⋌༼ •̀ ⌂ •́ ༽⋋");
    }
}

这样就是一个匿名对象

public static void main(String[] args) {
    new Person();//匿名对象
}

且匿名对象只能访问一次

public static void main(String[] args) {
    new Person().func();
    //如果再想访问name,需要再new一次
    System.out.println(new Person().name);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jSFwO82Z-1652411447475)(C:/Users/gu%27jiu/Pictures/c/20200811130123_5074f.png)]
匿名内部类

public static void main(String[] args) {
    new Person() {
        //匿名内部类,只能访问一次
        public void func() {
            System.out.println("⋌༼ •̀ ⌂ •́ ༽⋋");
        }
    }.func();
}

🌷(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))🌷

以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!下一篇文章会继续讲述Java的继承的相关知识,期待大家支持,谢谢~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值