Java 关键字解析:new、static 与 final
Java 关键字解析:new、static 与 final
dong4jnew
要谈 new 关键字, 就必须先说说构造方法, 它们 2 个就是一对好基友.
构造方法
定义
无参构造方法:
访问修饰符 类名(){ }
带参构造方法
访问修饰符 类名(参数列表){ }
说明
- 构造方法可以被重载;
- 没有返回类型 (void 也算返回类型, 只是没有返回值);
- 方法名和类名必须一样;
- 如果不写, 系统会默认提供一个无参构造方法;
- 如果写了, 系统就不会提供任何构造方法;
- 子类不能继承父类的构造方法;
- 接口没有构造方法;
作用
产生对象
new 一个对象的时候构造器所要做的事
- 创建空间;
- 划分属性;
- 初始化值;
- 执行构造方法内的语句;
实例化一个对象
用 new 关键字实例化一个对象时, 所用做的事:People p = new People();
- 加载 People,class 文件进入内存;
- 在栈内存为 p 变量申请一个空间 (4 个字节);
- 在堆内存为 People 对象申请空间 (大小为类中所有成员变量的大小);
- 对类中的成员变量进行默认初始化;
- (如果有) 对类的成员变量进行显示初始化;
- 有构造代码块, 先执行构造代码块中的语句;
- 执行构造方法中的语句;
- 把堆内存的引用赋给 p 变量;
static
(类级别的, 与对象无关)
可以修饰属性, 方法, 代码块和内部类;
static 修饰属性 (类变量)
- 那么这个属性就可以用
类名.属性名
来访问, 也就是使这个属性称为本领的类变量, 为本类对象所共享;
static 修饰方法 (静态方法)
- 会使这个方法称为整个类所公有的方法, 可以用
类名.方法名
访问; - static 修饰的方法, 不能直接访问本类中的非静态成员, 但本类的非静态方法可以访问本类的静态方法;
- 在静态方法中不能出现
this
关键字; - 父类中是静态方法, 子类中不能覆盖为非静态方法;
- 在符合覆盖规则的前提下, 在父子类中, 父类中的静态方法可以被子类中的静态方法覆盖, 但是没有多态;
- 在使用对象调用静态方法时, 其实是在调用编译时类型的静态方法;
static 修饰初始化代码块
- 此时初始化代码块叫做静态初始化代码块, 这个代码块只在类加载时被执行一次;
- 可以用静态初始化代码块初始化一个类;
- 动态初始化代码块, 些在类体的
{}
中, 这个代码块在生成对象时运行;
例子
1 | class StaticTest{ |
输出结果:
静态初始化代码块
x = 5
a = 3
b = 12
执行顺序
- 首先加载
StaticTest
类, 在静态区开辟空间, 给a
赋值为3
,b
赋值为0
; - 接着执行静态初始化块, 先打印一条语句, 然后把
a*4
的值赋给b
; - 然后把
main()
方法调入栈中,main()
调用method()
方法, 把3
传给x
; - 然后打印;
注意: 在一个 static 方法中引用任何实例变量都是非法的.
main() 中的 static
一般情况下,主方法是静态方法,所以可调用静态方法,主方法为静态方法是因为它是整个软件系统的入口,而进入入口时系统中没有任何对象,只能使用类调用。
说明
在有 static 定义的类的外面, static 方法和变量能独立于任何对象而被使用.
例如想从类的外部调用一个 static 方法:className.method();
或者调用一个 static 属性:className.属性名;
这就是 Jaba 如何实现全局功能和全局变量的一个控制版本.
例子
1 | class StaticDemo{ |
输出结果:
a = 42
b = 99
最后一点: static 成员不能被其所在 class 创建的实例访问的.
简单来说
- 如果不加 static 修饰的成员是对象成员, 也就是归每个对象所有;
- 加 static 修饰的成员是类成员, 就是可以由一个对象直接调用, 为所有对象公有.
从内存的角度来看 static
类加载的过程中, 类本身也是保存在文件中的 (字节码文件保存着类的信息),
java 通过 I/O 流把类文件读入 JVM, 这个过程称为类的加载.
JVM 会通过类路径 (classpath) 来找字节码文件.
需要的时候才会进行类的加载, 生成对象时是先加载后构造.
类变量, 会在加载时自动初始化, 初始化规则和实例变量相同.
注意:
类中的实例变量是在创建对象时被初始化的.
static 修饰的属性, 是在类加载时被创建并进行初始化,
类加载的过程只有一次, 也就是类变量只会被创建一次.
静态非静态成员的调用
- 静态成员能直接调用静态成员;
- 非静态成员不能调用静态成员, 能调用非静态成员;
原因:
从内存的角度, static 修饰的成员是随着类的加载而创建的,
如果此时用静态成员调用非静态成员, 则会报错,
因为在内存中还没有这个非静态成员;
非静态成员只有在实例化对象的时候才在内存中创建, 所以非静态成员可以调用静态成员.
说白了就一句话:
先出生的不能娶还没有出生的;(指腹为婚不在讨论的范围之内)
后出生的长大了可以娶先出生的 (现在很流行啊), 也可以娶一起出生的;
final
最终的意思, 不允许改变;
可以修饰变量, 方法和类.
final 修饰变量
- 一旦被 final 修饰的变量, 就会变成常量, 不能被重新赋值.
- 常量可以在初始化的时候直接赋值, 也可以在构造方法里赋值,
- 只能二选一, 不能为常量重新赋值;
- 常量没有默认值;
- 锁定栈, 使栈中的数据不可以改变;
- 静态常量只能在初始化时直接赋值;
关于引用的问题
问题: 使用 final 修饰的变量, 是引用不能变还是引用的对象不能变?
1 | final StringBuffer a = new StringBuffer("finalTest"); |
所以被 final 修饰的变量, 是引用变量不能变, 引用变量所指向的对象中的内容还是可以改变的.
final 修饰方法
被 final 修饰的方法就不能被子类重写.
final 修饰类
- 被 final 修饰的类将不能被继承.
- final 类中的方法也都是 final 的.
注意:
final 不能用来修饰构造方法.
public static final int MAX = 100;
常量属性定义为 static, 节约空间
final: 因为用 final 修饰, 所以是个常量
public: 因为是常量, 所以不能更改, public 修饰给别人看也没事
static: 共享, 这样就不用 new 出来才能使用;
补充说明:
内部类要访问局部变量时, 局部变量必须定义为 final.
为什么局部内部类不能访问没有被 final 修饰的局部变量?
从内存中看,当方法里的局部变量所在方法结束时,该变量即在栈内存中消失;而内部类其实是一个类,只有内存中对它的所有引用都消失后,该内部类才”死亡”,即内部类的生命周期可能比局部变量长。如果局部内部类能访问一般的局部变量,则在多线程中,可能当方法结束后,局部内部类 (是线程类时) 还在使用局部变量。为了避免方法内的变量脱离方法而存在的现象发生,于是 java 规定局部内部类不能访问一般的局部变量。但能访问被 final 修饰的局部变量。因为常量放在堆内存中的数据段的常量池中
finally
是异常处理语句的一个部分, 是异常的同意出口, 表示总是执行.
就算是没有 catch 语句同时又抛出异常的情况下,
finally 代码块仍然会被执行。
最后要说的是,finally 代码块主要用来释放资源,比如:I/O 缓冲区,数据库连接
finalize()
是 Object 类的一个方法, 在垃圾收集器执行的时候,
会调用被回收对象的此方法,
可以覆盖此方法提供垃圾收集时的其他资源回收, 例如关闭文件等.
JVM 不保证此方法总被执行.
abstract
- 抽象类不能实例化对象, 但是有构造方法, 抽象类中可以包含属性, 抽象类中的构造方法是用来初始化成员属性的;
- 所有抽象对象必须被重写, 除非子类也是抽象类;
- 可以存在没有抽象方法的抽象类;
- 有抽象方法的类必须定义为抽象类;
abstract type name_method();
- 只能修饰方法和类;
剩下的详细总结将在面向对象的特征中介绍.
instanceof
A instanceof B
A 对象是否是 B 对象的实例
1 | public boolean equals(Object obj){ |
this
- 对当前类的引用;
- 每个对象都有一个隐含的 this 变量, 它可以访问类的所有信息;
- 构造方法的相互调用, 必须放在构造方法内的第一个语句;
在重载的构造器中, 为了重复利用代码
1 | class Gril { |
super
this 和 super 详解
this
this.–> 此时的 this 代表当前对象, 可以操作当前类中的所有属性和方法; 也可以操作当前类父类访问修饰符允许的方法和属性super 代表当前对象的父类, 只能操作当前类父类中访问修饰符允许的属性和方法;
- this 不能看到的时候, super 也看不到, this 能看到的, super 还是看不到;
- 只有在发生重写的时候, 才用 super 调用当前对象的父类中同名的方法和属性;
- this() 调用本类的其他构造方法;
- super() 调用当前对象的父类的构造方法;
- 都只能放在构造方法的第一行;
- this() 没有默认;
- super() 为默认公共无参构造;
关于 super 更详细的介绍将在面向对象之继承中见到.