第五章 高级特性2

第五章

关键字static

类属性

和对象无关的属性叫做类属性

类属性作为该类各个对象之间共享的变量。在设计类时,

分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法

类方法

和对象无关的方法叫做类方法

如果方法与调用者无关,则这样的方法通常被声明为类方法,

由于不需要创建对象就可以调用类方法,从而简化了方法的调用

使用范围

在Java类中,可用static修饰属性、方法、代码块、内部类

static修饰特点

被修饰后的成员具备以下特点:

static可以修饰属性, 方法, 语句块, 内部类.

随着类的加载而加载, 因为静态成员隶属于类

优先于对象的存在而存在

被所有对象所共享

在其他类中可以直接通过类访问静态成员, 不需要创建对象.

静态属性

静态属性, 也称为类属性. 隶属于类. 会被所有对象共享, 在内存中的永久区的类模板中

非静态属性

类的成员属性

静态方法

静态方法, 也称为类方法, 和调用者无关, 调用不需要对象, 只通过类即可调用.

非静态方法

类的成员方法

静态

它的存在是确定的, 一份的. 类模板

非静态

它的存在是不确定的, 只要有new一个对象, 它的存在就会产生一套新的, 多份的.

工具方法

只需要通过类就能调用的静态方法. Math.random(), Integer.parseInt()

工具类

所有的方法都是工具方法的类就是工具类. 比如 Math. Arrays.....

静态环境和非静态环境

静态环境中可以直接访问非静态成员吗? 非静态环境中可以访问静态成员吗? 各自的原因是什么?

非静态环境中可以直接访问静态成员, 因为对象共享类模板.

静态环境不可以直接访问非静态成员, 因为非静态成员必须 要隶属于某个对象.

或者可以先创建对象, 再通过对象来访问非静态成员.

 

/**
*
* 静态 : 它的存在是确定的, 一份的. 类模板
* 非静态 : 它的存在是不确定的, 只要有new一个对象, 它的存在就会产生一套新的, 多份的.
*
* 类属性作为该类各个对象之间共享的变量。在设计类时,
* 分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
*
* 如果方法与调用者无关,则这样的方法通常被声明为类方法,
* 由于不需要创建对象就可以调用类方法,从而简化了方法的调用
*
* 使用范围:
*     在Java类中,可用static修饰属性、方法、代码块、内部类
*
* 被修饰后的成员具备以下特点:
*     随着类的加载而加载, 因为它存在于类模板中
*     优先于对象存在, 创建对象前先必须加载类
*     修饰的成员,被所有对象所共享, 因为所有对象都能找到它的对应的类模板
*     访问权限允许时,可不创建对象,直接被类调用
*
*工具方法
*
* 构造器不可以被static修饰, 因为构造器是为对象初始化, 是绝对的非静态概念.
*
* 非静态环境中可以直接访问静态成员, 原因是在非静态环境中, 也就是和对象相关的概念中, 类模板早已处理好.
* 在静态环境中却不可以直接访问非静态成员!! 因为静态方法没有调用者对象, 所以不可以使用this和super关键字
* 可以通过创建一个对象, 再通过这个对象来访问
*/
public class Employee {

   // 静态属性, 也称为类属性. 隶属于类. 会被所有对象共享, 在内存中的永久区的类模板中
   public static String company = "atguigu";
   private static int total = 1;
   // emp是什么? 是静态的类属性, 在永久区中. emp中保存了对象在GC区中的地址
   // 而emp永远不消失, 它指向的对象永远不会变垃圾!!!
   private static Employee emp = new Employee();

   // 静态方法, 也称为类方法, 和调用者无关, 调用不需要对象, 只通过类即可调用.
   public static void test1() {
       System.out.println(company);
       System.out.println("static test1()...");
  }

   public static void test2() {
       // 直接访问非静态成员不可以, 原因是此方法没有调用者对象的概念.
       /*
       this.id = 100;
       this.name = "某员工";
       this.age = 20;
       this.salary = 5000;
       System.out.println(super.hashCode());
       */
       emp.id = 100;
       emp.name = "某员工";
       emp.age = 20;
       emp.salary = 5000;
       System.out.println(emp);

       System.out.println("static test2()...");
  }

   // 非静态属性, 也称为对象属性, 在内存中的GC区中的对象内部.
   private int id; // 表示员工号, 自动生成.
   private String name;
   private int age;
   private double salary;

   public Employee() {}

   public Employee(String name, int age, double salary) {
       this.id = total++; // 自动生成!!
       this.name = name;
       this.age = age;
       this.salary = salary;
  }

   public int getId() {
       return id;
  }

   public void setId(int id) {
       this.id = id;
  }

   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       this.age = age;
  }

   public double getSalary() {
       return salary;
  }

   public void setSalary(double salary) {
       this.salary = salary;
  }

   @Override
   public String toString() {
       return "Employee{" +
               "id=" + id +
               ", name='" + name + '\'' +
               ", age=" + age +
               ", salary=" + salary +
               '}';
  }
}

 

单例

概念

单例 : 某个类只能有一个实例(对象).

分类

饿汉式单例,懒汉式单例

饿汉式单例

创建对象代价小时优选

  1. 不允许外部new对象, 封死new操作, 私有化构造器.

  2. 在类内部new唯一对象, 把对象地址保存到一个私有的静态属性中

  3. 再提供一个公共的静态方法, 用于获取唯一对象

懒汉式单例

创建对象太复杂时.

  1. 不允许外部new对象, 封死new操作, 私有化构造器.

  2. 声明私有静态属性, 是一个指向本类对象的引用, 但是不创建对象.

  3. 再提供一个公共静态方法, 当调用者第一次获取对象时, 才创建对象

 

package com.atguigu.javase.statictest;

import java.io.IOException;

/**
* 单例 : 某个类只能有一个实例(对象).
*
* 饿汉式单例 : 创建对象代价小时优选
*     1) 不允许外部new对象, 封死new操作, 私有化构造器.
*     2) 在类内部new唯一对象, 把对象地址保存到一个私有的静态属性中
*     3) 再提供一个公共的静态方法, 用于获取唯一对象
*
* 懒汉式单例 : 创建对象太复杂时.
*     1) 不允许外部new对象, 封死new操作, 私有化构造器.
*     2) 声明私有静态属性, 是一个指向本类对象的引用, 但是不创建对象.
*     3) 再提供一个公共静态方法, 当调用者第一次获取对象时, 才创建对象
*/
class Singleton1 {

   private static Singleton1 only = new Singleton1(); // 类加载时就创建唯一对象

   public static Singleton1 getInstance() {
       return only;
  }

   private Singleton1() {}
}

class Singleton2 {

   private static Singleton2 only; // 类加载时并不创建对象

   public static Singleton2 getInstance() {
       if (only == null) { // 第一次
           only = new Singleton2();
      }
       return only;
  }

   private Singleton2() {}
}

class Test {

   @Override
   public void finalize() {
       System.out.println("我要死了, ..... " + toString());
  }
}

public class SingletonTest {

   public static void main(String[] args) throws IOException {
       //new Runtime();
       Runtime rt1 = Runtime.getRuntime();
       Runtime rt2 = Runtime.getRuntime();
       System.out.println(rt1 == rt2);
       System.out.println("rt1.availableProcessors() = " + rt1.availableProcessors());
       System.out.println("rt1.freeMemory() = " + rt1.freeMemory());
       System.out.println("rt1.totalMemory() = " + rt1.totalMemory());
       //rt1.exit(0); // 直接无条件退出JVM, 最恐怖 .
       //System.out.println("lkajsdfljasdf");
       Test t = new Test();
       t = null;
       rt1.gc(); // 垃圾收集, 建议GC回收垃圾.
       rt1.exec("explorer http://www.taobao.com"); // 执行os命令
  }

   public static void main2(String[] args) {
       //new Singleton2();
       Singleton2 s1 = Singleton2.getInstance();
       Singleton2 s2 = Singleton2.getInstance();
       System.out.println(s1 == s2);
  }

   public static void main1(String[] args) {
       /*
       Singleton1 s1 = new Singleton1();
       Singleton1 s2 = new Singleton1();
       System.out.println(s1 == s2);
       */
       // new Singleton1();
       /*
       Singleton1 s1 = Singleton1.only;
       Singleton1.only = null;
       Singleton1 s2 = Singleton1.only;
       System.out.println(s1 == s2);
       */

       Singleton1 s1 = Singleton1.getInstance();
       Singleton1 s2 = Singleton1.getInstance();
       System.out.println(s1 == s2);
  }

}

类的成员之四:初始化块

静态代码块

用static 修饰的代码块

  1. 可以有输出语句。

  2. 可以对类的属性、类的声明进行初始化操作。

  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。

  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。

  5. 静态代码块的执行要先于非静态代码块。

  6. 静态代码块只执行一次

非静态代码块

没有static修饰的代码块

  1. 可以有输出语句。

  2. 可以对类的属性、类的声明进行初始化操作。

  3. 可以调用静态的变量或方法。

  4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。

  5. 每次创建对象的时候,都会执行一次。且先于构造器执行

注意:静态语句块和属性的显示赋值语句

  • 所有的static块和所有属性的显式赋值是平级的

  • 会按照顺序把所有的static块和所有的显式赋值语句 整合在一起, 形成一个特殊方法

    public static void <clinit>() {

    System.out.println("static 0 ....");

    total = 10000;

    company = "yewu";

    total = 1;

    System.out.println("static 1 ....");

    total = 100;

    System.out.println("static 2 ....");

    total = 1000;

    }

非静态语句块和属性的显示赋值

  • 所有的非静态块和所有的属性的显式赋值是同级别, 按顺序整合好, 再和所有构造器整合

  • 形成一系列重载的构造方法

    public void <init>(...) {

    非静态块和属性的显式赋值按顺序

    构造器中的代码

    }

    public void <init>(...) {

    非静态块和属性的显式赋值按顺序

    构造器中的代码 }

  • 静态块, 非静态块, 构造器 统称为初始化器 : 作初始化工作.

执行顺序

父类静态块——子类静态块——父类非静态块——父类构造器——子类非静态块——子类构造器

class Base {
   static int total = -100;
}
public class Employee extends Base {

   static final public String DEFAULT_COMPANY;

   static {
       DEFAULT_COMPANY = "yewu";
       System.out.println("static 0 ....");
       total = 10000;
       System.out.println(Base.total); // 必须要区分是父类的total还是本类的total
  }

   private static String company = DEFAULT_COMPANY;
   // 属性的声明肯定是最先执行. 显式赋值和上面的静态块之间是平级的顺序关系
   private static int total = 1;

   public static void test1() {
       System.out.println("公司 : " + company);
       //name = "张三", 不可以直接访问非静态属性, 必须先创建好对象.
  }

   static { // 静态语句块, 给类进行初始化工作, 所以也称为类的初始化器
       // 在类加载时执行仅有一次, 作用就是给类初始化.
       System.out.println("static 1 ....");
       total = 100;
       System.out.println(total);
  }

   static {
       System.out.println("static 2 ....");
       total = 1000;
  }


  {
       age = 100;
  }

   // final的特点就是一旦赋值就不允许修改! 像身份证号.
   private final int id; // 空final量, 很危险, 要求必须尽快赋值一次.
   private String name;
   private int age = 10;
   private double salary;

  { // 非静态块和构造器没有必然联系. 只要创建对象就一定要初始化的任务可以放在这里.
       this.id = total++; // 自动生成唯一的变化的id
  }

   public Employee() {
       System.out.println("Employee Employee()");
       age = 40;
  }

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

   public Employee(String name, int age, double salary) {
       //this.id = total++; // 自动生成唯一的变化的id
       this.name = name;
       this.age = age;
       this.salary = salary;
       System.out.println("Employee Employee(3)...");
  }

  {
       // 非静态块 是隶属于对象, 会在创建对象时执行仅有一次, 作用就是给对象进行初始化工作
       // 只要创建, 它一定执行, 并且要先于构造器执行.
       System.out.println("非静态块1...");
       age = 50;
  }
  {
       System.out.println("非静态块2...");
  }

   public int getId() {
       return id;
  }

   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       this.age = age;
  }

   public double getSalary() {
       return salary;
  }

   public void setSalary(double salary) {
       this.salary = salary;
  }

   @Override
   public String toString() {
       return "Employee{" +
               "id=" + id +
               ", name='" + name + '\'' +
               ", age=" + age +
               ", salary=" + salary +
               '}';
  }
}

 

关键字final

注意事项

final 修饰类表示这个类是终极类

final 修饰方法表示这个方法是终极完美方法, 不允许子类重写.

final 修饰变量表示这个量是最终量, 必须赋值仅有的一次, 不赋值不行, 赋值2次或以上也不行.

public static final : 全局常量 , public是公共的, static是通过类就能访问到, final是不能修改,很安全.

final class A {}
//class B extends A {}

class C {
   final void test1() {}
}
class D extends C {
   //@Override
   //public void test1() {}

}
public class FinalTest {

   public static void main(String[] args) {
       System.out.println(Math.PI);
       //Math.PI = 100;
  }
}

空final

空final量, 很危险, 要求必须尽快赋值一次.

public class Employee {
   private static int total = 1;
   
   private final int id; // 空final量, 很危险, 要求必须尽快赋值一次.
   private String name;
   private int age = 10;
   private double salary;
   
   public Employee () {
       this.id = total++;
  }
   
   public Employee(String name, int age, double salary) {
       this.id = total++; // 自动生成唯一的变化的id
       this.name = name;
       this.age = age;
       this.salary = salary;
       System.out.println("Employee Employee(3)...");
  }
   
    public int getId() {
       return id;
  }
       
   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       this.age = age;
  }

   public double getSalary() {
       return salary;
  }

   public void setSalary(double salary) {
       this.salary = salary;
  }

   @Override
   public String toString() {
       return "Employee{" +
               "id=" + id +
               ", name='" + name + '\'' +
               ", age=" + age +
               ", salary=" + salary +
               '}';
  }    
}

 

非静态块和final

public class Employee {
  private static int total = 1;
   
   private final int id; // 空final量, 很危险, 要求必须尽快赋值一次.
   private String name;
   private int age = 10;
   private double salary;
// final的特点就是一旦赋值就不允许修改! 像身份证号.
   
  { // 非静态块和构造器没有必然联系. 只要创建对象就一定要初始化的任务可以放在这里.
       this.id = total++; // 自动生成唯一的变化的id
  }
   
   public Employee () {}
   
   public Employee(String name, int age, double salary) {
       //this.id = total++; // 自动生成唯一的变化的id
       this.name = name;
       this.age = age;
       this.salary = salary;
       System.out.println("Employee Employee(3)...");
  }    
   public int getId() {
       return id;
  }
       
   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       this.age = age;
  }

   public double getSalary() {
       return salary;
  }

   public void setSalary(double salary) {
       this.salary = salary;
  }

   @Override
   public String toString() {
       return "Employee{" +
               "id=" + id +
               ", name='" + name + '\'' +
               ", age=" + age +
               ", salary=" + salary +
               '}';
  }    
}

 

抽象类与具体类

具体类

某种事物的描述

抽象类

某类不同种事物的描述

可以包含抽象方法

抽象类特点

  • 抽象类用abstract修饰

  • 抽象类可以包含抽象方法, 也可以不包含.

  • 可以包含普通的属性方法构造器

  • 抽象类不能创建对象 因为抽象方法

  • 具体类不能包含抽象方法

  • 抽象类不能创建对象, 不能实例化. 原因就是因为它里面的抽象方法, 万一创建了对象,就调用抽象方法. 无法执行.

抽象方法

作为父类类型应该具有的行为, 但是怎么个行为法不确定, 因为类型太模糊..

抽象方法 : 只有方法声明, 没有方法体的方法, 绝不能执行!!!!

注意

不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法.

public abstract class Pet {

   private String name;
   private int age;
   private double weight;

   public Pet() {}

   public Pet(String name, int age, double weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
  }

   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       this.age = age;
  }

   public double getWeight() {
       return weight;
  }

   public void setWeight(double weight) {
       this.weight = weight;
  }

   @Override
   public String toString() {
       return "Pet{" +
               "name='" + name + '\'' +
               ", age=" + age +
               ", weight=" + weight +
               '}';
  }

   public abstract void speak(); // 纯虚方法

   public abstract void eat();

}

子类与实现

具体子类不能包含抽象方法, 所以必须要重写所有抽象方法


// 具体子类不能包含抽象方法, 所以必须要重写所有抽象方法
public class Cat extends Pet {

   private String color;

   public Cat() {}

   public Cat(String name, int age, double weight, String color) {
       super(name, age, weight);
       this.color = color;
  }

   public String getColor() {
       return color;
  }

   public void setColor(String color) {
       this.color = color;
  }

   @Override
   public String toString() {
       return super.toString() + " Cat{" +
               "color='" + color + '\'' +
               '}';
  }



   @Override // 有没有实现 只看有没有{}
   public void speak() { // 子类 "实现" (implement) 了抽象方法
       System.out.println("喵喵喵...");
  }

   @Override
   public void eat() {
       System.out.println("小猫吃小鱼鱼");
  }

}
public class PetTest {

public static void main(String[] args) {
//Pet pet = new Pet();
//new Object();
//当我们看到抽象类型的引用时, 它一定是多态引用!!!!
Pet p = new Cat("小黄", 2, 3, "黄色");
p.speak(); // 虚拟方法调用
p.eat();
}
}

模板方法设计模式(TemplateMethod)

概念

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

解决的问题
  • 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。

  • 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。

abstract class Template{
public final void getTime(){
long start = System.currentTimeMillis();
code(); // 实际调用的一定是子类中的方法.
long end = System.currentTimeMillis();
System.out.println("执行时间是:"+(end - start));
}
public abstract void code(); // 暴露出去的方法, 子类必须要实现
}

class SubTemplate extends Template{ // 子类仍然保留了父类的行为模式, 并且更具体

public void code(){
for(int i = 0;i<10000;i++){
System.out.println(i);
}
}
}

public class TemplateTest {

public static void main(String[] args) {
Template tmp = new SubTemplate();
tmp.getTime();
}
}

抽象类应用

抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。

接口

接口 : 不同类不同种事物的共同行为的描述

 

类不可以继承接口, 接口也不可以继承类, 因为这2个东西没有关系.

 

只有一种关系 : 就是类实现接口

接口之间可以互相继承, 并且可以多重继承.

具体子类必须实现所有抽象方法.

具体子类既继承了抽象父类, 又实现多个接口, 通过接口的方式间接实现了多重继承

子类和父类的关系更亲密, 因为父类表达的是子类究竟是什么. 接口关系稍远, 只是表明

这个子类 又额外又多了一些功能.

 

接口用于表达某种能力(通常用形容词) 或表达标准和规范.

 

面向接口编程 : 无视子类的个性, 只是把它当作接口类型的对象来使用. 简化对象的使用.

子类对象是什么一点都不重要, 无论用哪个子类对象对于使用者没有区别. 降低对象的标准, 解除了耦合!!

 

子类可以实现(implements)接口, 必须要实现接口中的所有抽象方法

 

面向接口编程

: 无视子类的个性, 只是把它当作接口类型的对象来使用. 简化对象的使用.

子类对象是什么一点都不重要, 无论用哪个子类对象对于使用者没有区别. 降低对象的标准, 解除了偶合!!


public class FlyerTest {

// 面向接口编程 : 无视子类的个性, 只是把它当作接口类型的对象来使用. 简化对象的使用.
// 子类对象是什么一点都不重要, 无论用哪个子类对象对于使用者没有区别. 降低对象的标准, 解除了偶合!!
public static void main(String[] args) {
Flyer f = new Plane();
f.takeOff();

f = new Bird();

f.takeOff();
}

public static void main3(String[] args) {
Pet pet = new Bird("小黄", 3, 0.1, 50);
pet.speak();
pet.eat();

// 造型 : 把对象 "当作是" 某类型的对象, 造型的 本质就是切换一下看待对象的视角
if (pet instanceof Flyer) { // 判断左面的引用指向的对象实体 是否是 右面的类型的实例(对象)
Flyer f = (Flyer) pet;
f.takeOff();
f.fly();
f.land();
// f引用如果真的指向了对象, 一定是个对象, 只要是对象必须是Object的子类.
System.out.println(f); // toString()
}
}

public static void main2(String[] args) {
Pet pet = new Bird("小飞", 2, 0.2, 30);
pet.speak();
pet.eat();

//pet.takeOff();
//pet.fly();
//pet.land();
}

public static void main1(String[] args) {
//Flyer f = new Flyer();
//System.out.println("kkkkk");
Flyer f = new Plane();
f.takeOff(); // 虚拟方法调用!!!!
f.fly();
f.land();
}
}

 

 

// 具体子类必须实现所有抽象方法. // 具体子类既继承了抽象父类, 又实现多个接口, 通过接口的方式间接实现了多重继承 // 子类和父类的关系更亲密, 因为父类表达的是子类究竟是什么. 接口关系稍远, 只是表明 // 这个子类 又额外又多了一些功能.

代理

**
* 代理模式 : 把代理对象当作被代理对象来使用, 代理对象和被代理对象达到同样的标准.
*
* 应用场景1 : 使用者无法直接创建被代理对象.
* 应用场景2 : 统一对象所有被代理对象的方法进行升级处理. 无法直接修改被代理对象的类.
*
* 接口具有粘合作用, 可以把完全没有任何关系的对象联系起来.
*/
interface HouseRent {
void rent();
}
class FangDong implements HouseRent {
@Override
public void rent() {
System.out.println("我有房子要出租, 是婚房, 请爱护. 账号 : 234234234234234");
}
}
class FangDong2 implements HouseRent {
@Override
public void rent() {
System.out.println("我也有房子要出租, 刚死了2个人, 晚上要小心");
}
}
class LianJia implements HouseRent {

private HouseRent hr = new FangDong2();

@Override
public void rent() {
System.out.println("请交中介费20000"); // 增强
hr.rent(); // 原始调用. // 面向切面编程 : AOP.
System.out.println("及时交房租, 不然赶走..., 支持微信和支付宝"); // 增强
}
}

public class ProxyTest {
public static void main(String[] args) {
// 把代理对象当作被代理对象来使用, 面向接口 : 无论子类对象是什么都不care
HouseRent hr = new LianJia();
hr.rent();

工厂方法

package com.atguigu.javase.interfacetest;

/**
* 通过方法调用获取对象, 因为有可能对象的创建极其复杂, 构造器无法访问
* 工厂有2种模式, 一种是本类就是工厂, 另一种专门有工厂类.
*/
class Teacher {

public static Teacher getTeacher() {
return new Teacher();
}

public void work() {
System.out.println(this + "老师在工作");
}
}

class Factory {

public static Teacher getTeacher() {
return new Teacher();
}
}

public class FactoryTest {

public static void main(String[] args) {
Teacher t1 = Teacher.getTeacher();
t1.work();
Teacher t2 = Factory.getTeacher();
t2.work();
}
}

抽象类和接口的比较

比较项目抽象类接口
定义 某类不同种事物的抽象定义 不同类不同种事物共同行为的抽象定义
组成 属性, 方法, 构造器, 抽象方法 全局常量, 公共抽象方法
如何使用 被继承 被实现
两者关系 抽象类可以实现接 口 接口不会主动和类产生联系
常见设计模式 模板 代理模式
创建对象 不能创建, 多态引用指向子类对象 不能创建,多态引用指向子类对象
局限性 单继承 没有
实际应用 模板 能力, 标准规范
选择 优先选择接口  

 

类的成员之五:内部类

概念

内部类 : 在一个类中定义另外的类, 里面的是内部类, 外面的是外部类

在其他测试类中创建内部类对象

3. class A {

class B {}

}



main() {

A.B ab = new A().new B();

}

 

分类

  • 成员内部类 : 声明在类中方法外

    普通内部类 : 没有static修饰

    嵌套类 : 有static修饰

  • 局部内部类 : 声明在方法中

    普通局部内部类 : 在方法中声明的普通类.

    匿名内部类 : 没有类名 最重点!!!!!!

匿名内部类

匿名内部类 : 没有名字的内部类, 最重要, 绝大多数情况是和接口配合使用的.

  • 在方法中声明并同时创建对象, 因为没有类名.

  • 因为没有类名, 所以不能后期创建对象, 必须在声明时就同时创建唯一对象.

  • 适用于某个接口的实现子类对象的一次性使用. 不需要费劲写子类.

书写格式

new 父类构造器(...) || 接口() { 类体部分就是父类或接口的子类 }

 书写格式:Object o2 = new Object() {}; // 匿名内部类对象
********************************************************************
//具体实例
interface changge {
void sing (String song);
}

public class InnerTest {
public static void main(String[] args) {
changge c1 = new changge() {
@Override
public void sing(String song) {
System.out.println("唱一首" + song);
}
};
c1.sing("《十年》");// 虚拟方法调用

}
}

//具体过程分析
class NoName implements changge {
@Override
public void sing (String song) {
System.out.println("唱一首" + song);
}
changge c1 = new changge();
c1.sing("《十年》");
}

 

具体实例

class Outer {

private String name = "Outer";

public void test2() {
// 在外部类中可以直接创建内部类对象, 再通过对象访问对象成员
Inner1 i1 = this.new Inner1();
i1.test1();
}

// 普通成员内部类, 是非静态成员, 隶属于外部类的对象
protected class Inner1 {
//public static int id = 10; // 普通内部类不可以声明静态属性
private String name = "Inner1";

public void test1() {
System.out.println("name = " + name); // 就近原则
System.out.println("this.name = " + Inner1.this.name);
// 访问外部类的当前对象的name
System.out.println(Outer.this.name);
}
}

// 嵌套类
public static class Inner2 {

}
}
public class InnerTest {

public static void main(String[] args) {
Outer outer = new Outer();
outer.test2();

System.out.println("*************************************");
// 直接创建内部类对象
// 必须通过外部类对象.new 才能创建内部类对象
Outer.Inner1 oi1 = outer.new Inner1();

Outer.Inner1 oi2 = new Outer().new Inner1();
oi1.test1();

}
/**
* 内部类 :
*     成员内部类
*         普通内部类 : 没有static修饰
*             实现最夸张的对象关联, 外部类和内部类对象互相关联, 都可以
*             直接访问对方的私有成员.
*
*             实际使用尽量不要使用内部类, 而应该使用普通对象关联, 不会破坏封装性.
*         嵌套类 : 有static修饰
*     局部内部类
*         普通局部内部类 : 范围小, 只能在方法中使用
*         匿名内部类 : 没有名字的内部类, 最重要, 绝大多数情况是和接口配合使用的.
*             因为没有类名, 所以不能后期创建对象, 必须在声明时就同时创建唯一对象.
*             适用于某个接口的实现子类对象的一次性使用. 不需要费劲写子类.
*/
class Outer {

   private String name = "Outer";

   public void test2() {
       this.new Inner1().test1();
  }

   private void test3() {
       System.out.println("Outer.test3");
  }

   // 普通内部类 : 隶属于外部类对象
   public class Inner1 {

       private String name = "Inner1";

       public void test1() {
           System.out.println(name);
           System.out.println(Outer.this.name);
           test3();
      }
  }

   // 嵌套类 : 隶属于外部类, 和外部类是同级别
   public static class Inner2 {

       public static String name = "Inner2";

       private int id;

       public void test4() {
           System.out.println("Inner2.test4");
      }
  }
}

interface I1 {
   void hello(String s);
}
public class InnnerTest {
   public static void main(String[] args) {
       /*
       class NoName implements I1 {
           @Override
           public void hello(String s) {
               System.out.println("你好 " + s);
           }
       }
       I1 i1 = new NoName();
       */
       I1 i1 = new I1() {
           @Override
           public void hello(String s) {
               System.out.println("你好 " + s);
          }
      };
       i1.hello("李换英"); // 虚拟方法调用
  }
   public static void main3(String[] args) {
       // 匿名内部类 : 在方法中声明并同时创建对象, 因为没有类名.
       /*
       new 父类构造器(...) || 接口() {
           类体部分就是父类或接口的子类
       }*/
       Object o1 = new Object(); // 普通对象
       Object o2 = new Object() {}; // 匿名内部类对象
       System.out.println(o1);
       System.out.println(o2);
  }

   public static void main2(String[] args) {
       class Inner3 { // 普通局部内部类
           private int id;
           private String name;

           public Inner3(int id, String name) {
               this.id = id;
               this.name = name;
          }

           @Override
           public String toString() {
               return "Inner3{" +
                       "id=" + id +
                       ", name='" + name + '\'' +
                       '}';
          }
      }
       Inner3 inner3 = new Inner3(1, "小刚");
  }

   public static void main1(String[] args) {
       //new Inner3();
       Outer outer = new Outer();
       outer.test2();
       // 直接创建内部类对象
       Outer.Inner1 oi1 = outer.new Inner1();

       System.out.println(Outer.Inner2.name);
       Outer.Inner2 oi2 = new Outer.Inner2();
       oi2.test4();

  }
 

 

posted @ 2022-05-05 16:27  叶舞  阅读(85)  评论(0)    收藏  举报