static定义
static用于修饰方法、成员变量等成员。
static修饰的成员表明它属于这个类本身,而不属于该类的单个实例,通常把static修饰的成员变量和方法也成为类变量、类方法。
不使用static修饰的普通方法、成员变量则属于该类的单个实例,而不属于该类,通常把不适用static修饰的成员变量和方法也称为实例变量、实例方法。
成员变量的初始化和内存中的运行机制
1 | class Person{ |
![[7b799a4b-42ef-48c9-87bb-7a5a832f72a0.png]]
![[79702b73-cc00-4a82-bc07-4d4cf732107b.png]]
局部变量的初始化和内存中的运行机制
局部变量定义后,必须经过显式初始化后才能使用,系统不会为局部变量执行初始化。这意味着定义局部变量后,系统并为这个变量分配内存空间,直到等到程序为这个变量赋初始值时,系统才会为局部变量分配内存,并将初始值保存到这块内存中。
与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。如果局部变量是基本类型的变量,则直接把这个变量的值保存在该变量对应的内存中;如果局部变量是一个引用类型的变量,则这个变量里存放的是地址,通过该地址引用到该变量实际引用的对象或数组。
栈内存中的变量无需系统垃圾回收,往往随方法或代码块的运行结束而结束。因此,局部变量的作用域是从初始化该变量开始,直到该方法或代码块运行完成而结束。因为局部变量只保存基本类型的值或者对象的引用,因此局部变量所占的内存区通常比较小。
Java引用变量两个类型
- 编译时类型,由声明变量时使用的类型决定
- 运行时类型,由实际赋给该变量的对象决定
如果编译时类型和运行时类型不一致,就可能出现所谓的多态
多态
1 | class BaseClass{ |
初始化块
1 | class Root{ |
控制台打印结果:
类初始化阶段,先执行最顶层父类的静态初始化块,然后依次向下,直到执行当前类的静态初始化快
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
对象初始化阶段,先执行最顶层父类的初始化块、最顶层父类的构造器,然后依次向下,直到执行当前类的初始化块、当前类的构造器
Root的普通初始化块
Root的无参数构造器
Mid的普通初始化块
Mid的无参数构造器
Mid的带参数构造器,其参数值:疯狂Java讲义
Leaf的普通初始化块
执行Leaf的构造器
Root的普通初始化块
Root的无参数构造器
Mid的普通初始化块
Mid的无参数构造器
Mid的带参数构造器,其参数值:疯狂Java讲义
Leaf的普通初始化块
执行Leaf的构造器
Integer
1 | Integer ina = 2; |
java.lang.Integer源码:
1 | static final Integer[] cache = new Integer[-(-128) + 127 + 1]; |
系统把一个-128~127之间的整数自动装箱成Integer实例,放入了cache数组中缓存起来。因此-128~127之间的同一个整数自动装箱成Integer实例时,永远都是引用cache数组的同一个数组元素,所以它们全部相等,但每次把一个不在-128~127范围内的整数自动装箱成Integer实例时,系统总是重新创建一个Integer实例。
== 和 equals 方法
当使用 == 来判断两个变量是否相等时,如果两个变量是基本类型变量,且都是数值类型,则只要两个变量的值相等,就将返回true;
对于两个引用类型变量,只有他们指向同一个对象时,== 判断才会返回true。
equals()方法
使用这个方法判断两个对象相等的标准与使用 == 运算符没有区别,同样要求两个引用变量指向同一个对象才会返回true。
“hello”和new String(“hello”)区别
当java程序直接使用形如”hello”的字符串直接量(包括可以在编译时就计算出来的字符串值)时,JVM将会使用常量池来管理这些字符串;当使用new String(“hello”)时,JVM会先使用常量池来管理”hello”变量,再调用String类的构造器来创造一个新的String对象,新创建的String对象被保存在堆内存中。换句话说,new String(“hello”)一共产生了两个字符串对象。
1 | public class StringCompareTest{ |
宏替换
final修饰符的一个重要用途就是定义“宏变量”。
当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那么这个final变量本质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
1 | final int a = 5; |
对于这段代码,变量a其实根本不存在,当程序执行System.out.println(a);时,实际转换为执行System.out.println(5);
1 | String s1 = "疯狂Java"; |
接口和抽象类
相同点:
- 接口和抽象类都不能被实例化,他们都位于继承树的顶端,用于被其他类实现和继承
- 接口和抽象类都可以包含抽象方法,普通子类必须实现这些抽象方法
不同点:
1.设计目的上
接口体现的是一种规范。
对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式来提供);
对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务(就是如何来调用方法)。
抽象类作为多个子类的共同父类,他体现的是一种模板式设计。
抽象类作为多个子类的抽象父类,可以被当成系统实现过程中的中间产品,它已经实现了系统的部分功能(哪些已经提供实现的方法)
2.用法上
接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;
抽象类则完全可以包含普通方法。
接口里只能定义静态常量;
抽象类里既可以定义普通成员变量,也可以定义静态常量。
接口里不包含构造器;
抽象类可以包含构造器,并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
接口里不能包含初始化块;
抽象类可以包含初始化块。
一个类最多只能有一个直接父类,包括抽象类;
但一个类可以直接实现多个接口,通过实现多个接口弥补Java单继承的不足。