修饰符
final
- 对于基本类型,final使数值不变
- 对于引用类型,final使引用不变,也就是不能引用其他对象:对其初始化之后便不能再让其指向另一个对象。
- 对于方法,则声明的方法不能被子类重写(但是可以被子类访问,前提是父类的final方法不是private的)
- 对于final类,则不允许被继承
- 在final修饰变量的时候:
- 成员变量:
- 必须在定义时候或者在初始化时候进行赋值,final变量一旦被初始化赋值之后,就不能再被赋值了。
- 被final修饰的变量有三种赋值方式。
- 被final static修饰的变量有两种赋值方式。

- 局部变量:
- 在使用之前被初始化即可

- 成员变量:

final作用于引用中

final int b = 1; b之后是不可以改的;那如果是这样的例子,A对象的x可以改吗
是可以改的,这也是引用类型的那一种,final是作用在A对象的引用上,而不是作用在A对象的数据成员x上,因此是可以改的。
final+private
final方法不能被子类重写,那么问题来了,为啥这样可以

在private 方法隐式地被指定为 final的时候,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法并不是重写了基类方法,而是在子类中定义了一个新的方法。(反正子类也无法访问父类的private方法)
final与普通变量的区别
public class FinalAndVariableDifference {public static void main(String[] args) {String a = "helloWord1";final String b = "helloWord";String F = "helloWord";String c = b + 1; // 编译期间就初始化了String e = F + 1; // 运行时候初始化System.out.println((a == c));System.out.println((a == e));}}

在String中”==”判断的是不是一个对象,指向的是一个地址
对于a,它在编译时候就已经确定放入了常量池中,b同理.
重新定义一个字符串String c = helloword1首先会从常量池中检查有没有helloword1这个常量,没有,就先入常量池,再返回。有,就直接返回!比如第一次定义String a = helloword1,就会先入池再从池中返回,第二次定义String c = helloword1,这个值就是直接从池中返回的2
由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b替换为它的值(这种情况我们成为编译器的优化)。而对于变量F的访问却需要在运行时才能连接确定,所以返回false
只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,那是不是只要是被final修饰的变量就会进行优化呢?
当然不是!比如下面的这段代码就不会进行优化:
public class FinalAndVariableDifference {public static void main(String[] args) {String a = "helloWord2";final String b = getHello(); //尽管是final修饰,但不会进行优化,因为它要运行时初始化才被确定String c = b + 2;System.out.println((a == c));}public static String getHello() {return "helloWord";}}运行结果:false
被final修饰的变量不一定会进行优化,优化的前提是编译时就已经能够确定!
static
https://blog.csdn.net/qq_44543508/article/details/102736466
static的主要意义是在于:
- 创建独立于具体**对象**独立于具体。
- 用来形成静态代码块以优化程序性能
- static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。
- 被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。
- static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
- 为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。
在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。
应用场景
如果某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量。
静态变量:
static修饰的成员变量叫做静态变量【也叫做类变量】,静态变量是属于这个类,而不是属于是对象。
实例变量:
没有被static修饰的成员变量叫做实例变量,实例变量是属于这个类的对象的。
static是不允许用来修饰局部变量,因为局部变量属于方法(在静态方法也不能使用局部static变量).
静态变量与实例变量的区别
静态变量:
静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。
实例变量:
每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员
public class StaticDemo {static int value = 666;public static void main(String[] args) throws Exception{new StaticDemo().method();}private void method(){int value = 123;System.out.println(this.value);}}
运行结果: 666
里面定义的value=123只是一个很普通的局部变量而已 。和成员变量没有半毛钱关系 。和静态变量也没有关系。
例子主要目的是说明一下this也是可以访问static的变量的!!!
构造方法不是静态方法!
static VS this
可以在静态方法内使用this或者super关键字吗
只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,可以说static和this和super是互相矛盾的存在。
静态代码块不能访问普通变量
静态代码块执行顺序
public class IntegerTest {static {System.out.println("static");}{System.out.println("构造代码块"); // 如果不实例化对象(也就是不执行构造方法),构造代码块是不会执行的!}public IntegerTest() {System.out.println("IntegerTest.IntegerTest");}public static void main(String[] args) {new IntegerTest();}}static构造代码块IntegerTest.IntegerTest--------------public class IntegerTest {static {System.out.println("static");}{System.out.println("普通代码块");}public IntegerTest() {System.out.println("IntegerTest.IntegerTest");}public static void main(String[] args) {// new IntegerTest(); 不实例化, 则{System.out.println("普通代码块");}不会执行}}static
进阶版
基本上代码块分为三种:Static静态代码块、构造代码块、普通代码块
代码块执行顺序**静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块**
继承中代码块执行顺序:父类静态块——>子类静态块——>父类代码块——>父类构造器——>子类代码块——>子类构造器 (可以结合类的加载器的初始化阶段进行学习)
- 构造代码块
只要该类实例了一个对象,构造代码就执行一次,利用每次创建对象的时候都会提前调用一次构造代码块特性,所以它可以做统计创建对象的次数功能。
1、构造代码块在创建对象时被调用,每次创建对象都会调用一次
2、构造代码块优先于构造函数执行,同时构造代码块的运行依赖于构造函数
“依赖”可理解为如果不实例化对象(也就是不执行构造方法),构造代码块是不会执行的!
3、构造代码块在类中定义
package com.gx.initializationblock;public class Initializationblock {int intA;int intB;public Initializationblock() {System.out.println("无参构造器00000000");}public Initializationblock(int a) {System.out.println("一个参数的构造器");}{intA = 10;intB = 15;System.out.println("构造初始化块11111");}{System.out.println("构造初始化块22222");}{System.out.println("构造初始化块33333");}//静态初始化块static {System.out.println("静态初始化块01010101");}static {System.out.println("静态初始化块0202020202");}public void method(){{System.out.println("普通初始化块");}}}package com.gx.initializationblock;/* 初始化块一* 因为静态块是在类的初始化阶段完成的,* 因此在创建某个类的第二个对象时,该类的静态块就不会执行了** 在单个类中,静态初始化块,初始化块,构造器* 多个类的继承中初始化块、静态初始化块、构造器的执行顺序在继承中,先后执行父类A的静态块,父类B的静态块,最后子类的静态块,然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器*/public class Demo1 {public static void main(String[] args) {Initializationblock initializationblock = new Initializationblock();initializationblock.method();System.out.println("------------");//多打印几个对象的目的是:好看出Static静态代码块只执行一次!!!Initializationblock initializationblock2 = new Initializationblock(); //因为静态块是在类的初始化阶段完成的,因此在创建某个类的第二个对象时,该类的静态块就不会执行了initializationblock2.method();Initializationblock initializationblock3 = new Initializationblock();initializationblock3.method();}}结果:静态初始化块01010101静态初始化块0202020202构造初始化块11111构造初始化块22222构造初始化块33333无参构造器00000000普通初始化块------------构造初始化块11111构造初始化块22222构造初始化块33333无参构造器00000000普通初始化块构造初始化块11111构造初始化块22222构造初始化块33333无参构造器00000000普通初始化块
继承中的代码块执行顺序
假设B继承A,C继承B.
.
多个类的继承中初始化块、静态初始化块、构造器的执行顺序为:先后执行父类A的静态块,父类B的静态块,最后子类C的静态块,然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器.
class A {static {System.out.println("A.static initializer");}{System.out.println("A.instance initializer");}public A() {System.out.println("A.A");}}class B extends A{static {System.out.println("B.static initializer");}{System.out.println("B.instance initializer");}public B() {System.out.println("B.B");}}class C extends B{static {System.out.println("C.static initializer");}{System.out.println("C.instance initializer");}public C() {System.out.println("C.C");}}public class IntegerTest {public static void main(String[] args) {C c = new C();C c1 = new C();}}A.static initializerB.static initializerC.static initializerA.instance initializerA.AB.instance initializerB.BC.instance initializerC.CA.instance initializerA.AB.instance initializerB.BC.instance initializerC.CProcess finished with exit code 0
静态内部类
静态内部类与非静态内部类之间存在一个最大的区别:
非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围,但是静态内部类却没有。
没有这个引用就意味着:
1、它的创建是不需要依赖外围类的创建。
2、它不能使用任何外围类的非static成员变量和方法。
例子:静态内部类实现单例模式
public class Singleton {// 声明为 private 避免调用默认构造方法创建对象private Singleton() {}// 声明为 private 表明静态内部该类只能在该 Singleton 类中被访问private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getUniqueInstance() {return SingletonHolder.INSTANCE;}}
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。
只有当调用 getUniqueInstance()方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。
抽象类和接口
抽象类和抽象方法都使用 abstract 关键字进行声明,如果一个类中包含抽象方法(没有抽象方法也可以声明,但是没必要),那么这个类必须声明为抽象类,并且抽象类和普通类最大的区别是,抽象类不能被实例化,只能被继承。
- 抽象类可以实现代码的重用,模板方法设计模式是抽象类的一个典型应用.
- 比如初始化的时候需要根据不同活动进行不同功能开启判断,那么就可以定义一个活动的基类,将功能开启判断做成一个抽象方法,让所有活动都继承这个基类,然后在子类自行重写功能开启判断。
- 接口可以说成是抽象类的一种延伸,接口中的所有方法都必须是抽象的。实际上,接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。
- 如果想要实现一个不可变类:
- (1)类声明为final,不可以被继承。- (2)所有成员变量定义为私有和final(private final)。- (3)不提供改变成员变量的方法。- (4)通过构造器初始化成员,若构造器传入引用数据类型需要进行深拷贝。
- Java8开始,接口也可以拥有默认的方法实现了,为啥Java团队要做这个支持呢?实际上,在Java上深耕多年的老司机其实都被接口没有默认实现搞过,维护成本太高了,你想想,如果你现在有100个类实现了一个接口,而在这个时候,需要给接口新增一个方法,特么Java8之前不允许给接口加默认实现,所以你就得改100个类,这特么不得搞出人命吗?
- 抽象类和接口默认实现的区别
- 抽象类,是一个模板,是一个半成品,让子类去完善。接口是一个规范,是更抽象的,让类按照接口的规范实现方法。
- 接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用.
- 答案:
- 抽象类可以有构造方法,接口中不能有构造方法。
- 抽象类中可以有普通成员变量,接口中没有普通成员变量
- 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
- 抽象类中可以包含静态方法,接口中不能包含静态方法
- 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
- 一个类可以实现多个接口,但只能继承一个抽象类。
- 抽象类可以有构造方法,接口中不能有构造方法。
private|protect
作者:程序员库森
链接:https://www.nowcoder.com/discuss/597041
来源:牛客网
- default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。注意:不能修饰类(外部类)
- public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。注意:不能修饰类(外部类)。




