7-关键字(final,static)内部类

本文详细解释了Java中的final、static关键字的用法,以及内部类(普通、静态和匿名)的特点。讨论了如何设计不可变类和单例模式,强调了类加载的线程安全性和懒加载特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看王争专栏关键字与内部类总结

目录

目录

一,final

1)修饰类

2)修饰方法

3)修饰变量

3-1)类的成员变量

3-2)函数局部变量

3-3)函数的参数

3-4)final修饰的引用变量

4)根据String的思路,如何设计一个不可变类

二,static

1)static变量

2)static方法

3)代码块

4)嵌套类

5)final与static修饰上的一些区别

三,内部类/嵌套类(重)

1)普通内部类

2)静态内部类(与普通内部类的区别)

3)匿名内部类

4)参数的访问

四,思考题

1,看代码回答问题

2,证明外部类的加载不会引起内部类的加载


一,final

final关键词可以修饰类,方法,变量(类的属性,局部变量,函数参数)

1)修饰类

final修饰的类不可被继承。

2)修饰方法

final修饰的方法不可被重复,但反向思考,子类可以将父类的非final方法重写成final方法。

3)修饰变量

final修饰的变量叫final变量或者常量,final变量只能被赋值一次,一旦赋值就不可以再变。

final变量有三种:类的成员变量,函数局部变量,函数的参数

3-1)类的成员变量

类的成员变量被赋值有两种方式。

1,在成员变量声明时

2,在构造器中

个人觉得还有第三种

实例化如下

private final String s;
{
    s = "dfsa";
}

3-2)函数局部变量

函数的局部变量需要在被使用前赋值。

所以也是在声明时赋值,或者声明时不赋值,在使用前赋值也可

3-3)函数的参数

参数肯定是赋了的值的,只是final修饰的参数不可变。

另外也分基本类型和引用类型

3-4)final修饰的引用变量

final修饰的引用变量,指的是指定了哪个对象就不能更改指向别的对象,但是对象里面的属性是可以修改的。

比如 final char[] chars = new char[34];

里面的值是可以变的

而为什么String里面的值不可更改呢,其实是因为没有提供直接修改的方法,同时其返回引用类型的方法,返回的是副本,不是value本身。所以保证了String的不可变。

4)根据String的思路,如何设计一个不可变类

1,将类使用final修饰,可以避免继承类重写方法,修改属性,如下:

2,类的所有属性被final修饰,被赋值一次后就无法再修改,但是如果可以保证没有方法修改这个属性,也可以不设为final,如String中的hash,没有单独方法给其修改值,另外在hashcode中,因为value的不可变,其值也不会更改,可以不设为final

3,如果类的属性是引用类型,那么当其能过方法返回时,应该返回其复副本而不是本身,避免被修改,如:

二,static

static可以修饰变量,方法,代码块,嵌套类

1)static变量

static只能是类的成员变量,被static修饰的类的成员变量也叫静态变量,被static修饰的变量属于类而非对象,对这个类的所有对象共享。

对于静态变量,即可以通过类直接访问,也可以通过对象访问,如下:

同时被final和static修饰的变量叫静态常量,一般需要大写

静态变量与类的代码一样,存储在方法区中。

2)static方法

static修饰的方法属于类而非对象

static修饰的方法也是类的所有对象共享,可以不用创建对象,直接使用。

比如String的valueOf()方法。

另外需要注意的静态方法,只能访问静态变量。静态方法不能访问非静态变量与调用非静态方法,反过来非静态方法可以访问静态变量和调用静态方法。

3)代码块

有时候static定义的变量没法简单赋值完成,比如hashMap这类的,可以使用静态代码块,如下:

如果有多个,静态代码块执行的顺序与定义的顺序相同。

4)嵌套类

嵌套类也叫内部类,是指定义在一个类中的类,承载内部类的类,叫外部类。

而被static修饰的内部类叫静态内部类。

5)final与static修饰上的一些区别

1,final修饰的变量可以是类的成员变量,函数的局部变量,函数参数,而static只能是类的成员变量

2,final和static都可以修饰类,但static只能修饰嵌套类/内部类。

三,内部类/嵌套类(重)

内部类是指定义在一个类中的类,也叫内部类,承载内部类的类,叫外部类,常见的内部类有普通内部类,静态内部类,匿名内部类三种。内部类在编译成字节码后,会独立于外部类,成为一个单独的class文件,命名方式为外部类名$内部类名.class,而匿名内部类因为没有名字,会生成:外部类名$(序号).class,这个序号是1,2,3,4这样,表示这个匿名内部类是外部类的第几个匿名内部类。

1)普通内部类

1)如何创建

即直接定义在类中的类,如下:

2)如何调用

内部类如果只是被外部类调用,不管被private修饰还是public修饰,都是先创建外部类,再创建内部类即可,不用管权限。

但是如果要被外部类以外的类调用,那么要么被public修饰(protected等也可以在相应权限的地方被创建),要么继承一个外部接口,然后再创建,如下:

2)静态内部类(与普通内部类的区别)

1,静态内部类与普通类的区别

1)在访问权限上,内部类与外部类中方法拥有相同的访问权限,即像静态方法只能访问静态变量与调用静态方法,而普通方法可以访问所有变量以及调用所有方法一样。静态内部类只能访问外部类的静态变量与调用静态方法,而普通内部类可以访问外部类的所有属性和方法(包括private修饰的)。

2)静态内部类可以拥有静态方法和静态变量,而普通类内部类不能拥有静态变量和静态方法,但是在jdk16有了改变,普通类也可以拥有静态变量和静态方法。

3)静态内部类的创建与普通内部类存在不同,普通内部类必须先创建外部类的对象,然后通过对象创建内部类,而静态内部类,不需要创建外部类的对象,可以直接创建(且只能直接创建,通过对象创建会报错)。

2,静态内部类如何被外部调用

静态内部类也可以被private, protected,public,与无修饰四种修饰词来修饰,如果被private修饰,那么要么只能在外部类内部使用,要么也与普通内部类一样,通过实现一个接口,同时在外部类拥有创建这个内部类的方法。如:

List<Integer> list = Arrays.asList(new Integer[]{1,2,3,4,5});
list.add(3);      、//会报错,因为这个内部的ArrayList没有重写add方法。

即下面这个被private修饰的类,可以实现接口和继承类。

上面是在Arrays定义了一个实现了接口(AbstractList实现了List)的静态内部类,下面是提供了创建该类的方法。

如果是public的,或者另外两种修饰词在拥有权限的地方都是以下面方式创建。

即 外部类.内部类 instance = new 外部类.内部类()方式创建。

3)匿名内部类

在使用多线程时多使用匿名内部类,如下:

匿名内部类的权限与使用它的函数相同,即如果是静态方法,那么只能访问外部类的静态变量和静态方法,如果是非静态方法,那么可以访问外部类的所有变量和方法,哪怕是private修饰的也可以。

注意如果是局部变量,内部匿名类只能访问final修饰的局部变量。这是因为参数的传递是值传递,假如内部类对局部变量做了修饰,但是此时修改的只是内部类的这个变量,对外部类的变量本身是没有影响的(上面的c),这就很违反认知。所以被设计成只能访问final修饰的变量,内外保持一致。

关于匿名内部类访问局部变量,在jdk1.8做了优化,不再需要显示的用final修饰,但是事实上底层还是加了final的,如果只是访问不会报错,但一旦有修改操作,编译器还是会报错,即对局部变量还是需要是final修饰的或事实上不会修改的即可。参考如下:

匿名内部类访问局部变量是否需要 final 修饰(Java Kotlin 对比)_java局部变量不用写final-优快云博客

如下out不报错,a报错了,原因是在后面加了a的修改操作。

去掉上面的a = a + 3;报错就不会有了。

4)参数的访问

1,同样的参数名,即内,外部类都有,根据就近原则直接用访问的是内部类的

2,内部类用this.field,调用的是内部类的

3,外部类名.this.field,调用的是外部类的

四,思考题

1,看代码回答问题

前置知识:java的类加载是线程安全的(loadClass处用了synchronized上锁

),而类的加载是懒加载,只有当类被使用时(创建对象,调用静态方法)才会被加载,另外外部类的加载并不会引起内部类的加载。

1) 为什么这样实现的单例是线程安全的?

1,而上面的代码一旦被调用就会引起加载,而加载的同时完成了对类的创建,一个类只能被加载一次,上面的getInstance被调用时,访问了SingletonHolder的静态变量,触发加载SingletonHolder类。

2) 为什么这样实现的单例支持延迟加载?

因为类的加载本身就是懒加载。

3) 为什么SingletonHolder设计为静态内部类,是否可以是普通内部类?

普通内部类不能拥有静态变量与静态方法,但是要保证在类的加载过程中创建好instance,那么instance必须是静态的,所以内部类也必须是静态内部类。

4) 为什么将SingletonHolder设计为private的,而不是public?

防止被外部类以外的类所调用,即只能通过调用外部类的getInstance方法来触发内部类的加载,同时初始化内部类的静态变量instance。

综上:以上的单例模式设计,利用了类的加载是线程安全的,同时也是懒加载,只有当被使用时才会被加载。再利用单例模式的通用特性,私有构造方法,公有静态方法,保证了只有在调用getInstance方法时才会触发类的加载,而同时触发内部静态类的加载,即保证了线程安全,只有一个例,又保证了延迟加载;因为只有让instance在类加载时创建才是安全的,那么instance需要是静态的,所以需要是内部静态类,同时内部类不需要被别的地方调用,设置成private即可。

2,证明外部类的加载不会引起内部类的加载
public class InintOuterNoInitInter {


    static {
        System.out.println("外部类");
    }


    static void main(String[] args) {
        System.out.println("开始测试");
        InintOuterNoInitInter clazz = new InintOuterNoInitInter();
        System.out.println("开始内部类");
        InintOuterNoInitInter.Innter inter = new InintOuterNoInitInter.Innter();
        System.out.println("结束");


        List<Integer> list = Arrays.asList(new Integer[]{1,2,3,4,5});
//        list.add(3);


//        InintOuterNoInitInter.Innter inter2= clazz.new Innter();   //此处报错,不可这样用


        InintOuterNoInitInter.Innter inter1 = new InintOuterNoInitInter.Innter();
    }


    protected static class Innter{
         static {
             System.out.println("内部类");
        }


    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值