类的成员之四:代码块
(重要性相较于属性、方法、构造器差一些)
作用
用来初始化 类、对象 的信息
分类(是否有static修饰)
静态代码块
- 内部可以输出语句
- 随着类的加载而执行,而且只执行一次
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
- 静态代码块的执行要优先于非静态代码块的执行
- 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块
- 内部可以输出语句
- 随着对象的创建而执行
- 每创建一个对象,就执行一次非静态代码块
- 作用:可以在创建对象时,对 对象的属性 等进行初始化(再赋值)
- 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
- 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法(全都可以调用)
练习
实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:
总结:由父及子,静态先行。
//LeafTest.java
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
//super();
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
//new Leaf();
}
}
//构造器leaf先行,
//调用super,
//去到Leaf父类Mid,调用Mid构造器
//调用this构造器,构造器默认有个super()构造器,
//去到Root,调用Root构造器
//同理也有super构造器,最后去到Object类
//开始加载类,同时加载静态结构,因此先输出各个类的静态结构
//再输出非静态结构
//最后是输出构造器
//如果执行下一条new Leaf()语句,则少执行各个类的静态结构,因为只执行一次
属性的赋值顺序
①默认初始化
②显式初始化/⑤在代码块中赋值
③构造器中初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
- 执行的先后顺序:① - ② / ⑤ - ③ - ④
类的成员之五:内部类
定义
Java允许将 一个类A 声明在 另一个类B 中,则类A就是内部类,类B 就是外部类
内部类的分类
- 可以调用外部类的结构
- 可以被static修饰
- 可以用四种权限修饰符修饰
- 可以用final修饰,表示此类不能被继承,若不用final,就可以继承
- 可以用abstract修饰
成员内部类
class B{
int age;
public static void sleep(){};//静态方法
public void eat(){//非静态方法
sout("吃");
}
static class A{//用static修饰成员内部类,同样适用static的规则
public void show(){
sout("展示");
eat();//调用外部类的非静态方法,报错
sout(age);//调用外部类的属性,报错
sleep();//调用外部类的静态方法,与 B.this.sleep(); 语句相同
}
}
protected class C{
}
private class D{
}
}
静态成员内部类
非静态成员内部类
class B{
static class A{
}
class c{
}
}
局部内部类
方法内
代码块内
构造器内
public void method(){//方法
//局部内部类
class AA{
}
}
{//代码块
//局部内部类
class BB{
}
}
public Person(){//构造器
//局部内部类
class CC{
}
}
内部类应该关注以下三个问题
- 如何实例化成员内部类的对象
- 如何在成员内部类中区分调用外部类的结构
- 开发中局部内部类的使用
public class A{
psvm{
B.C cc = new B.C();//创建 静态的成员内部类
cc.c1();
B b = new B();//创建 非静态的成员内部类
B.D dd = b.new D();
dd.d1();
}
}
class B{
static class C{
public void c1(){
sout("C1");
}
}
class D{
public void d1(){
sout("d1");
}
}
}
//开发中局部内部类的使用
public class InnerClassTest1 {
//开发中很少见
public void method(){
//局部内部类
class AA{
}
}
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
//方式一:
// class MyComparable implements Comparable{
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// }
// return new MyComparable();
//方式二:
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
注意点:
-
成员内部类和局部内部类,在编译以后,都会生成字节码文件。
格式:成员内部类:外部类 内 部 类 名 . c l a s s 局 部 内 部 类 : 外 部 类 内部类名.class 局部内部类:外部类 内部类名.class局部内部类:外部类数字 内部类名.class -
在局部内部类的方法中(比如:show如果调用外部类所声明的方法(比如:method)中的局部变量(比如:num)的话,要求此局部变量声明为final的。
jdk 7及之前版本:要求此局部变量显式的声明为final的
jdk 8及之后的版本:可以省略final的声明
public class InnerClassTest{
public void method(){
int num = 10;
class AA{//局部内部类
public void show(){
// num = 20;会报错,因为此时的num已经是用final修饰过了
sout(num);
}
}
}
}