Mind Map

类的结构 - 图1

1. 属性

1.1 实例变量

实例变量也叫成员变量, 为了封装性, 一般加 private 修饰符以阻止外部直接访问,当然还有 static 静态变量(类变量)和 final 常量等。

成员变量可以进行显式赋值,也可以不这样做。

成员变量在类加载阶段会首先将字节全刷为 0,对应于 int0char'\u0000'long0lfloat0fdouble.0booleanfalse,以及引用数据类型的 null。之后再刷入显式赋值的值。之后再调用构造函数。

  1. private char ch;
  2. public static int count = 0;
  3. public static final double PI = 3.141592653;

1.2 类变量

类的成员中, static 修饰的类的变量为静态变量, 或类变量. 类变量由该类的所有实例共享, 不用创建对象就可以被访问.

2. 代码块

2.1. 构造代码块

构造代码块的出现本质是为了提取构造函数的共同代码, 减少各个构造函数的代码而产生的.
**

  1. public class Client {
  2. {//构造代码块
  3. System.out.println("执行构造代码块");
  4. }
  5. }


理解构造代码块:**

  1. 构造代码块的作用是给对象进行初始化.
  2. 对象一建立就运行构造代码块了,而且优先于构造函数执行。这里要强调一下,有对象建立,才会运行构造代码块,类不能调用构造代码块的,而且构造代码块与构造函数的执行顺序是前者先于后者执行。
  3. 构造代码块与构造函数的区别是:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。

注意:

  1. 可以有输出语句.
  2. 可以对类的属性, 类的声明进行初始化操作.
  3. 可以调用静态的变量或方法.
  4. 若有多个非静态的代码块, 那么按照从上到下的顺序依次执行.
  5. 每次创建对象的时候, 都会执行一次, 且先于构造器执行.

构造代码块是依托于构造函数, 也就是说在通过 new 关键字生成一个实例的时候, 相当于把代码块加到构造器最前面, 会先执行构造代码块, 然后再执行其他代码.

而我们知道任何构造器, 如果里面有 this(...) , 这个时候编译器会足够聪明, 不再给其前面添加构造区代码块, 而是跳转到 this(...) 直到构造器中没有 this() , 这个时再添加构造代码块.

而当有继承关系时, 如果有 super(...) 则直接跳转到父类构造器, 如果是 this(...) 同样先跳转到没有 this(...) 的构造器, 这个时候构造器首行默认添加 super() , 从此入口跳转到父类的构造器, 同样先执行父类的构造代码块, 执行完之后再执行父类构造器, 再回来, 接着才执行子类的构造代码块, 最后执行子类构造器. 这一部分可参见对象的创建过程.

2.2. 静态代码块

静态代码块的出现本质是为了给类初始化的. 用 staitc 声明, jvm 加载类时执行, 仅执行一次.
**

class A{
    static{//静态代码块
    }
}

理解静态代码块:

  1. 它是随着类的加载而执行,只执行一次,并优先于主函数。具体说,静态代码块是由类调用的。类调用时,先执行静态代码块,然后才执行主函数的。
  2. 静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的。
  3. 静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别。
  4. 一个类中可以有多个静态代码块.

静态代码块在类加载时执行, 同样注意:

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  5. 静态代码块的执行要先于非静态代码块。
  6. 静态代码块只执行一次

以下例子:

public class Test{
staitc int cnt=6;
static{
      cnt+=9;
}
public static void main(String[] args) {
      System.out.println(cnt);
}
static{
      cnt/=3;
}
}

运行结果:

5

3. 构造器

类还包含方法,在之前部分介绍过,方法也可以通过修饰符修饰。类中还有特殊的方法——构造方法,或构造器(constructor)。

构造器本质是一个方法,但是有其特殊性:

  1. 方法名和类名一致,唯一允许首字母大写的方法名;
  2. 没有返回值声明,甚至连 void 也不允许;
  3. 不能被关键字 staticfinalsynchronizenativeabstract 等修饰 ;
  4. 不能像普通方法一样随意调用,只能调用一次。某个对象的生命周期中只能调用一次。

需要注意的是:

  1. Java语言中,每个类都至少有一个构造器。
  2. 默认构造器的修饰符与所属类的修饰符一致。
  3. 一旦显式定义了构造器,则系统不再提供默认构造器。
  4. 一个类可以创建多个重载的构造器,一般我们需要提供空参构造器和全参构造器。
  5. 父类的构造器不可被子类继承。

构造器重载,还可以连环调用,但是一定要保证有一个构造器中是没有 this(…),防止无限递归。

举例:以下空参构造出 name = "Amos", age = 24Person 对象,而也可以通过 new Person(Cathy, 23); 来创建对象。

public class Person{
    private String name;
    private int age;

    public Person(){
        this("Amos", 24);
    }

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

4. 静态方法

没有对象的实例时, 可以用 ClassName.method()的形式访问由 static 标记的类方法.
需要注意的是:

  1. 在 static 方法内部只能访问类的 static 属性和方法, 不能访问类的非 static 属性和方法.
  2. 因为不需要实例就可以访问 static 方法, 因此 static 方法内部不能有 this, 也不能有 super.
  3. 重载的方法需要同时为 static 的或者非 static 的.

5. 内部类

在Java中, 允许一个类的定义位于另一个类的内部, 前者称为内部类(inner class), 后者称为外部类.

注意:

  1. 内部类一般用在定义它的类或语句块之内, 在外部引用它时必须给出完整的名称;
  2. 内部类的名字不能与包含它的类名相同;
  3. 内部类可以使用外部类的私有数据,因为它是外部类的成员,同一个类的成员之间可相互访问. 而外部类要访问内部类中的成员需要: 内部类.成员 或者 内部类对象.成员 .

    5.1. 成员内部类

    成员内部类, 隶属于外部类对象的. 在内部类中可以随意访问外部类成员:

    class Outer { // 顶级类只能被public和default修饰
    
     private int num = 10;
    
     // 和对象关联类似, 但是比对象关联好用, 因为可以访问私有成员. 对象关联只能使用关联对象的公共成员.
     class Inner1 { // 支持所有访问权限修饰符
         // 成员内部类不允许声明静态属性.
         private int num = 100;
    
         public int getNum() {
             return num;
         }
    
         public void inner1Test1() {
             // this可以加类限定, 更清晰明确
             System.out.println("成员内部类的属性 : " + Inner1.this.num); // 100
             System.out.println("成员内部类访问外部类属性 : " + Outer.this.num); // 10
             outerTest2(); // 可以访问私有方法
         }
     }
    
     public void outerTest1() {
         System.out.println("外部类方法");
         // 如果想要调用内部类方法
         Inner1 inner1 = this.new Inner1(); // 通过当前类对象new一个内部类对象
         inner1.inner1Test1();
         System.out.println(this.num); // 10
         System.out.println(inner1.num); // 100
     }
    
     private void outerTest2() {
         System.out.println("私有方法");
     }
    
     // 嵌套类, 加static修饰, 本质上和外部类是平级的
     // static仅仅表示它和外部类的关系
     public static class Inner2 {
         public static String name; // 可以定义静态变量
    
         private int id;
    
         public int getId() {
             return id;
         }
    
         public void setId(int id) {
             this.id = id;
         }
     }
    }
    

5.2. 局部内部类

5.2.1 普通局部内部类

普通局部内部类, 类的定义写在方法中, 不可以加访问控制修饰:

public class InnerClass{
    public static void main(String[] args) {
        // 普通局部内部类
        final class Inner3 {
            private int id;
            @Override
            public String toString() {
                return "Inner3{" +
                        "id=" + id +
                        '}';
            }
        };
        Inner3 inner3 = new Inner3();
        inner3.id = 200;
        System.out.println(inner3);
    }
}

5.2.2 匿名内部类

匿名内部类, 没有类名, 不能后期创建对象, 在声明的同时必须马上创建唯一对象.

匿名内部类通常就是和接口配合(或实现父类), 接口中的方法通常也不多, 格式如下:

new 父类|接口() {
    类体部分, 类体相当于new后面的父类或接口的实现子类.
};

举例如下:

interface MyInterface {
    String getInfo();
}

interface MyInterface2 {
    void test();
}

public class InnerTest {
    @Test
    public void anonymousInnerClassTest1() {
        // 什么情况下使用匿名内部类? 临时的想要用某个接口的对象.而且对象也是一次性使用
        // class Xxx implements MyInterface{};
        // MyInterface mi = new Xxx(); 相当于以上两个语句合体.
        new MyInterface2() {// 因为没有类名, 所以必须多态.
            @Override
            public void test() {
                System.out.println("匿名内部类的test");
            }
        }.test(); // 匿名内部类的匿名对象, 一次性使用

        // 这里看上去是 new Object 对象, 实际上是创建了一个直接子类, 可以在子类中重写或添加新的方法.
        Object o = new Object() { // 匿名内部类是Object的直接子类
            public String toString() {
                return "我是匿名内部类对象";
            }
        };
        System.out.println(o);
    }
}

5. JavaBean

JavaBean 是一种Java语言写成的可重用组件, 是指符合如下标准的 java 类 :

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有属性,且有对应的get、set方法

用户可以使用 JavaBean 将功能、处理、值、数据库访问和其他任何可以用 java 代码创造的对象进行打包,并且其他的开发者可以通过内部的 JSP 页面、Servlet、其他 JavaBean、applet 程序或者应用来使用这些对象。用户可以认为 JavaBean 提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。