Java_面向对象下(4)

abstract

抽象类可以没有抽象方法,但有抽象方法的类一定是抽象类,不能实例化

abstract 方法没有方法体,目的是让子类继承并重写,但 final 不让子类重写,private 不能让子类访问,所以 abstract 不能与 static, final, private 共存

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test {
  public static void main(String[] args) {
    //Animal is abstract; cannot be instantiated
    //Animal a = new Animal(); 
    Animal a = new Cat();
    a.eat();
  }
}

abstract class Animal {
  public abstract void eat();
  //抽象类虽然不能实例化,但可以有构造方法
  //将会在子类中调用,所以可以实现一些通用的构造逻辑
  public Animal() {
    System.out.println("Animal");
  }
}

class Cat extends Animal {
  @Override
  public void eat() {
    System.out.println("cat eat");
  }
}

interface

接口是只包含常量和方法声明的特殊抽象类,没有变量和方法的实现

在接口中定义的变量默认被 public、static、final 修饰,定义的方法默认被 public、abstract 修饰

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
  public static void main(String[] args) {
    Inter i = new InterImpl();
    i.print();
    System.out.println(Inter.num);
  }
}

interface Inter {
  //接口没有构造方法
  /*public static final*/ int num = 10;
  /*public abstract*/ void print();
}

class InterImpl implements Inter {
  @Override
  public void print() {
    //num = 20; //报错 num被final修饰不能修改
    System.out.println(num);
  }
}

内部类

普通类只有 public 和 default 两种访问权限,而内部类可以有全部访问权限

内部类可以直接访问外部类的属性,包括私有,而外部类访问内部类的属性,必须先创建内部类对象

调用方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test {
  public static void main(String[] args) {
    Outer o = new Outer();
    o.print();  //20
    //外部类名.内部类名 = 外部类对象.内部类对象
    Outer.Inner oi = o.new Inner();
    oi.print(); //10
  }
}

class Outer {
  private int numO = 10;
  public class Inner {
    private int numI = 20;
    public void print() {
      //内部类能通过外部类类名.this 访问外部类的(私有)成员
      System.out.println(Outer.this.numO);
    }
  }
  public void print() {
    //外部类要访问内部类的(私有)成员需要先创建内部类的实例对象
    System.out.println(new Inner().numI);
  }
}

局部内部类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test {
  public static void main(String[] args) {
    Outer o = new Outer();
    o.func(); //num = 10
  }
}

class Outer {
  public void func() {
    //被内部类访问的局部变量会被拷贝一份到内部类中
    //如果局部变量被修改了就会出现数据不一致的情况,所以需要用final修饰
    //Java8新增了Effectively final,不用再手动地添加final了
    /*final*/int num = 10;
    class Inner {
      public void print() {
        System.out.println(num);
      }
    }
    Inner i = new Inner();
    i.print();
    //num = 20;
    //所以不能修改num的值,修改了就会报错
    //Variable 'num' is accessed from within inner class,
    //needs to be final or effectively final
  }
}

匿名内部类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
  public static void main(String[] args) {
    Outer o = new Outer();
    o.method();
  }
}

class Outer {
  class Inner {
    public void print() {
      System.out.println("Inner");
    }
  }
  public void method() {
    //有名内部类
    Inner i = new Inner();
    i.print();
    //匿名内部类
    new Inner().print();
  }
}

可以用于简单地实现接口 interface

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Test {
  public static void main(String[] args) {
    new Inter() {
      @Override
      public void print() {
        System.out.println("InterImpl");
      }
    }.print();
  }
}

interface Inter {
  void print();
}

实现多继承

Java 中的继承只能是单继承,但可以通过实现多个接口或者内部类继承其他类的方式来实现多继承

接口 interface 的方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test {
  public static void main(String[] args) {
    Person p = new Person();
    p.swim();
    p.run();
  }
}

interface Swim {
  void swim();
}
interface Run {
  void run();
}

class Person implements Swim, Run {
  @Override
  public void swim() {
    System.out.println("swim");
  }
  @Override
  public void run() {
    System.out.println("run");
  }
}

内部类的方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Person {
  public void swim() {
    new Swim() {
      @Override
      public void swim() {
        System.out.println("swim");
      }
    }.swim();
  }
  public void run() {
    new Run() {
      @Override
      public void run() {
        System.out.println("run");
      }
    }.run();
  }
}

代码块

局部代码块

在方法内部,调用其所在的方法时就执行,局部代码块执行完后立刻释放空间,所以可通过局部代码块控制变量的生命周期,对于执行完就不需要的变量可以随即释放,避免生成过多局部变量导致栈内存不足

1
2
3
4
5
6
7
8
9
public class Test {
  public static void main(String[] args) {
    {
      int num = 1;
      num += 1;
    }
    //System.out.println(num); //报错,找不到num
  }
}

构造代码块

在类成员位置,用于把多个构造方法共同的部分提取出来,共用构造代码块,在 new 对象时自动调用,且每次调用构造方法前都会先执行构造代码块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
  private int i;
  {
    i = 1; //在构造代码块中初始化成员变量
    System.out.println(i); //1
    int i = 2; //代码块里的变量和成员变量不冲突
    System.out.println(i); //2,会优先使用代码块的变量
  }
  int j = 3;
  {
    System.out.println(j); //3
    //上一个构造代码块中定义的局部变量i执行完就释放了
    System.out.println(i); //1
  }
  Test() {
    System.out.println("构造方法");
  }
  public static void main(String[] args) {
    Test t = new Test();
  }
}

静态代码块

在类成员位置,被 static 修饰的代码块,用于对类进行初始化,new 对象时自动调用,且只调用一次

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
  Test() {
    System.out.println("构造方法");
  }
  {
    System.out.println("构造代码块");
  }
  static {
    //静态代码块只会调用一次
    System.out.println("静态代码块");
  }
  public static void main(String[] args) {
    Test t1 = new Test();
    Test t2 = new Test();
  }
}
//静态代码块
//构造代码块
//构造方法
//构造代码块
//构造方法

代码块执行顺序

静态成员变量/静态代码块 => 构造代码块 => 构造方法,其中静态成员变量和静态代码块根据代码的顺序执行,局部代码块在调用其所在方法时才调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Test {
  //第一行静态成员变量将创建Test对象,所以会执行一次blockA
  public static Test t1 = new Test();
  {
    System.out.println("blockA");
  }
  //静态代码块只执行一次
  static {
    System.out.println("blockB");
  }
  static {
    //给未定义的变量赋值编译器通过
    i = 20;
    //System.out.println(i);
    //非法向前引用,不能在同一个代码块中使用向前引用
  }
  public static int i = 10;
  static {
    i = 20;
    System.out.println(i);
  }
  
  public static void main(String[] args) {
    Test t = new Test();
  }
}
//blockA
//blockB
//20
//blockA
//第一次 blockA 是给静态成员变量 t 初始化时执行的
//第二次 blockA 是创建 t 引用指向的对象时执行的

以上内容是玉山整理的笔记,如有错误还请指出