java基础~面向对象(下)

目录

关键字static

static关键字的引入

static关键字的使用

单例(Singleton)设计模式

单例模式的优点:

饿汉式单例模式

懒汉式单例模式

单例设计模式的使用场景

理解main方法的语法

类的成员之四:代码块

代码块的分类

代码块与构造器执行的优先级

对属性赋值的优先级

关键字:final

抽象类与抽象方法(关键字abstract)

abstract关键字的使用

abstract使用上的注意点

抽象类的应用

抽象类练习

抽象类的匿名子类

接口(inferface)

接口的使用

接口的应用

接口和抽象类有什么联系和区别?

Java8接口新特性

类的成员之五:内部类


关键字static

static:静态的

static关键字的引入

​ 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。

static关键字的使用

static可以用来修饰:属性、方法、代码块、内部类

  1. 使用static修饰属性:静态变量(或类变量);属性按照是否使用static修饰,可分为:静态属性 和非静态属性(实例变量)

  • 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。

  • 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。

 public class StaticTest {
      public static void main(String[] args) {
          
          Chinese.nation = "中国";
          
          
          Chinese c1 = new Chinese();
          c1.name = "姚明";
          c1.age = 40;
          c1.nation = "CHN";
          
          Chinese c2 = new Chinese();
          c2.name = "马龙";
          c2.age = 30;
          
          //编译不通过
          //Chinese.name = "张继科";
  ​
          System.out.println(c2.nation);
          //会打印o1.nation的值,因为在定义Chinese类的时
          //侯,有关键字static
          
      }
  }
          
  class Chinese{
      String name;
      int age;
      static String nation;
  }
  • static修饰属性的其他说明:

    • 静态变量随着类的加载而加载,实例变量随着对象的创建而加载。静态变量可以通过"类.静态变量"的方式进行调用

    • 静态变量的加载要早于对象的创建,因为类的加载早于对象的创建。

    • 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。

    • 类变量(静态变量)可以用类调用,也可以用对象调用

      实例变量不可以用类调用,但是可以用对象调用。

  • 静态属性举例:System.out; Math.PI;

类变量与实例变量的内存解析

栈空间中存放局部变量;

堆空间中存放new出来的对象或数组;

方法区中存放的是类的加载信息,静态域,常量池;

  Chinese.nation = "中国"
  通过 类.静态变量  方式进行调用,
  静态变量随着类的加载而加载,首先方法区的类会进行加载
  在方法区的静态域里产生nation静态属性,在没有赋值之前的初始化值为full,执行赋值语句之后把"中国"赋值给nation。
 Chinese c1 = new Chinese();
  c1.name = "姚明";
  c1.age = 40;
  在栈空间里声明局部变量c1,在堆空间里new一个首地址值为0X7788的对象c1,让栈空间里的局部变量c1指向堆空间里的对象c1,此时堆空间里的对象c1里会伴着对象的创建声明Chinese类里的实例变量name和age;赋值之后name的值是姚明,age的值是40;
 Chinese c2 = new Chinese();
  c2.name = "马龙";
  c2.age = 30;
  在栈空间里声明局部变量c2,在堆空间里new一个首地址值为0X8899的对象c2,让栈空间里的局部变量c2指向堆空间里的对象c2,此时堆空间里的对象c2里会伴着对象的创建声明Chinese类里的实例变量name和age;赋值之后name的值是马龙,age的值是30;

  c1.nation = "CHN";
  类变量(静态变量)可以用类调用,也可以用对象调用
  c1.nation就是通过对象调用nation属性
  此时方法区的静态域里的nation的值将被修改为“CHN”
c2.nation = "CHINA";
  类变量(静态变量)可以用类调用,也可以用对象调用
  c2.nation就是通过对象调用nation属性
  此时方法区的静态域里的nation的值将被修改为“CHINA”

        2.使用static修饰方法:静态方法

  • 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用

  • 静态方法可以用类调用,也可以用对象调用

    非静态的方法不可以用类调用,但是可以用对象调用。

  • 静态方法中,只能调用静态的方法或属性;非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性

  public class StaticTest {
      public static void main(String[] args) {
          
          Chinese.nation = "中国";
          
          
          Chinese c1 = new Chinese();
          c1.name = "姚明";
          c1.age = 40;
          c1.nation = "CHN";
          
          Chinese c2 = new Chinese();
          c2.name = "马龙";
          c2.age = 30;
          c2.nation = "CHINA";
  ​
          c1.eat();
          
          Chinese.show();
          //编译不通过
  //      Chinese.eat();非静态方法只能通过对象调用
  //      Chinese.info();非静态方法只能通过对象调用
      }
  }
  ​
  class Chinese{  
      String name;
      int age;
      static String nation;
      
      public void eat(){
          System.out.println("中国人吃中餐");
          //可以调用非静态结构
          this.info();
          System.out.println("name :" +name);
          //可以调用静态结构
          walk();
          System.out.println("nation : " + nation);
      }
      
      public static void show(){
          System.out.println("我是一个中国人!");
          //不能调用非静态的结构
  //      eat();
  //      name = "Tom";
          //可以调用静态的结构
          System.out.println(Chinese.nation);
          walk();
      }
      
      public void info(){
          System.out.println("name :" + name +",age : " + age);
      }
      
      public static void walk(){
          
      }
  }

        3.static关键字使用注意点

  • 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。

  • 在静态的方法内,不能使用this关键字、super关键字

        4.开发中,如何确定一个属性是否要声明为static的?

  • 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。

  • 类中的常量也常常声明为static

        5.开发中,如何确定一个方法是否要声明为static的?

  • 操作静态属性的方法,通常设置为static的

  • 工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections

单例(Singleton)设计模式

设计模式:

​ 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。通俗来讲就是我们所说的”套路”。

单例设计模式:

​ 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

单例模式的两个实现形式: 饿汉式 ,懒汉式

饿汉式: 一开始就造对象(会占用内存)

  • 坏处:对象加载时间过长。

  • 好处:饿汉式是线程安全的

懒汉式: 啥时候用啥时候造对象

  • 好处:延迟对象的创建。

  • 目前的写法坏处:线程不安全。--->到多线程内容时,再修改

单例模式的优点:

​ 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

饿汉式单例模式

 public class SingletonTest1 {
      public static void main(String[] args) {
          
          Bank bank1 = Bank.getInstance();
          Bank bank2 = Bank.getInstance();
          
          System.out.println(bank1 == bank2);//true
      }
  }
  ​
  //饿汉式
  class Bank{
      
      //1.私有化类的构造器
      private Bank(){
          
      }
      
      //2.内部创建自己类的对象,也相当于自己类的属性
      //4.要求此对象也必须声明为静态的
      private static Bank instance = new Bank();
      
      //3.提供公共的静态的方法,返回类的对象,相当于Bank类对象属性的get方法
      public static Bank getInstance(){
          return instance;
      }
  }

懒汉式单例模式

 public class SingletonTest2 {
      public static void main(String[] args) {
          
          Order order1 = Order.getInstance();
          Order order2 = Order.getInstance();
          
          System.out.println(order1 == order2);//true
          
      }
  }
  ​
  ​
  class Order{
      
      //1.私有化类的构造器
      private Order(){
          
      }
      
      //2.声明当前类对象,没有初始化
      //4.此对象也必须声明为static的
      private static Order instance = null;
      
      //3.声明public、static的返回当前类对象的方法
      public static Order getInstance(){
          
          if(instance == null){
              
              instance = new Order();
              
          }
          return instance;
      }
      
  }

单例设计模式的使用场景

  • Runtime类

  • 网站的计数器,一般也是单例模式实现,否则难以同步。

  • 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。

  • 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。

  • Application也是单例的典型应用

  • Windows的Task Manager(任务管理器)就是很典型的单例模式

  • Windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

理解main方法的语法

由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。又因为main()方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。

main()方法的使用:

  1. main()方法作为程序入口,不需要返回值

  1. main()也是一个普通的静态方法

  2. main()方法可以作为我们与控制台交互的方式,(之前,我们都用的Scanner):右键Run As—Run Configurations(配置)—Arguments

类的成员之四:代码块

一个类里会有属性(变量),方法,构造器,代码块,内部类;其中前三种最常用。本章讲解代码块(又称初始化块)。

格式:

  static {
  //代码块如果有修饰符的话,只能使用static.
  //代码块的作用:用来当前的初始化类、对象
  ​
  }

代码块的分类

  1. 静态代码块

  • 内部可以有输出语句

    • 随着类的加载而执行,而且只执行一次

    • 作用:初始化类的信息

    • 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行

    • 静态代码块的执行要优先于非静态代码块的执行

    • 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构

2.非静态代码块

  • 内部可以有输出语句

  • 随着对象的创建而执行

  • 每创建一个对象,就执行一次非静态代码块

  • 作用:可以在创建对象时,对对象的属性等进行初始化

附加:可以对对象属性的方式和顺序

  • 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行

  • 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法

  static{
      静态代码块
  }
  ​
  {
      非静态代码块
  }

代码块与构造器执行的优先级

 //总结:由父及子,静态先行
  class Root{
      static{
          System.out.println("Root的静态初始化块");
      }
      {
          System.out.println("Root的普通初始化块");
      }
      public Root(){
          //找父类Objcet
          super();
          System.out.println("Root的无参数的构造器");
      }
  }
  class Mid extends Root{
      static{
          System.out.println("Mid的静态初始化块");
      }
      {
          System.out.println("Mid的普通初始化块");
      }
      public Mid(){
          //通过super调用父类中无参数的构造器
          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(); //先找Lesf类的构造器
          System.out.println();
          new Leaf();//因为静态代码块随着类的加载而执行,而且只执行一次,所以第二次执行new Leaf()的时候不会再执行静态方法块
      }
  }
  ​
  Root的静态初始化块
  Mid的静态初始化块
  Leaf的静态初始化块
  Root的普通初始化块
  Root的无参数的构造器
  Mid的普通初始化块
  Mid的无参数的构造器
  Mid的带参数构造器,其参数值:我是参数值
  Leaf的普通初始化块
  Leaf的构造器
  ​
  Root的普通初始化块
  Root的无参数的构造器
  Mid的普通初始化块
  Mid的无参数的构造器
  Mid的带参数构造器,其参数值:我是参数值
  Leaf的普通初始化块
  Leaf的构造器

对属性赋值的优先级

对属性可以赋值的位置:

①默认初始化

②显式初始化/在代码块中赋值

③构造器中初始化

④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值

具体代码实现参考java基础~面向对象之封装~构造器

关键字:final

在Java 中声明类,变量和方法时,可使用关键字final来修饰 ,表示 “最终的 ”。

final关键字的性质:

  1. final 用来修饰一个类:此类不能被其他类所继承。比如:String类、System类、StringBuffer类

  2. final 用来修饰方法:表明此方法不可以被重写。比如:Object类中getClass():获取当前对象所属的类。

  3. final 用来修饰变量:此时的"变量"就称为是一个常量

  • final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化。如果属性有final关键字修饰,声明属性的时候如果没有赋值,也就是默认初始化赋值,语法会报错。

  • final修饰局部变量: 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。

    注: static final 用来修饰属性:全局常量

抽象类与抽象方法(关键字abstract)

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

abstract关键字的使用

abstract:抽象的

abstract可以用来修饰的结构:类、方法

  1. abstract修饰类:抽象类

  • 此类不能实例化

  • 抽象类中一定有构造器,用abstract修饰的类不能再实例化,但是它的子类如果没有拿abstract修饰的子类可以实例化,所有抽象类的构造器是便于子类实例化时调用(涉及:子类对象实例化的全过程)

  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

        2.abstract修饰方法:抽象方法

  • 抽象方法只有方法的声明,没有方法体

  • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。

  • 如果父类是抽象类,并且有抽象方法,子类去继承父类的时候,要么把子类也设置成抽象类,要么重写父类中所有的抽象方法。

  • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化。

    若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰。

abstract使用上的注意点

  1. abstract不能用来修饰:属性、构造器,代码块等结构

  2. abstract不能用来修饰私有方法、静态方法、final的方法、final的类

抽象类的应用

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

例如:卡车和驳船都属于交通工具,但是我们不知道卡车的燃油效率怎么计算,所以我们可以把交通工具类设置为抽象类,交通工具类里的计算燃油效率的方法也设置成抽象方法,不能实例化,然后在交通工具类的子类卡车类和驳船类中重写父类的关于燃油计算的方法。

 public abstract class Vehicle{
      public abstract double calcFuelEfficiency();//计算燃料效率的抽象方法
  }
  public class Truck extends Vehicle{
      public double calcFuelEfficiency( ){
      //写出计算卡车的燃料效率的具体方法
      }
  }   
  public class RiverBarge extends Vehicle{
      public double calcFuelEfficiency( ) {
      //写出计算驳船的燃料效率的具体方法
      }
  }

关于抽象类的问题

问题1:为什么抽象类不可以使用final关键字声明?

答:抽象类鼓励子类去继承父类并重写方法,但是final关键字是不允许类再去继承的。

问题2:一个抽象类中可以定义构造器吗?

答:可以定义构造器,抽象类虽然不能造对象,但是子类可以造对象,子类还需要父类中的属性和方法

问题3:是否可以这样理解:抽象类就是比普通类多定义了抽象方法,除了不能直接进行类的实例化操作之外,并没有任何的不同?

答:是

抽象类练习

编写一个Employee类,声明为抽象类,包含如下三个属性: name,id,salary。提供必要的构造器和抽象方法: work()。对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。

Employee类:

  public abstract class Employee {
      
      private String name;
      private int id;
      private double salary;
      public Employee() {
          super();
      }
      public Employee(String name, int id, double salary) {
          super();
          this.name = name;
          this.id = id;
          this.salary = salary;
      }
      
      public abstract void work();
  }

Manager类:

  public class Manager extends Employee{
      
      private double bonus;//奖金
  ​
      public Manager(double bonus) {
          super();
          this.bonus = bonus;
      }
  ​
      public Manager(String name, int id, double salary, double bonus) {
          super(name, id, salary);
          this.bonus = bonus;
      }
  ​
      @Override
      public void work() {
          System.out.println("管理员工,提供公司运行的效率");
      }
  }

抽象类的匿名子类

目的:省事

PersonTest类:

 /*
   * 抽象类的匿名子类
   */
  public class PersonTest {
      
      public static void main(String[] args) {
          
          method(new Student());//非匿名类 匿名对象
          
          Worker worker = new Worker();
          method1(worker);//非匿名的类 非匿名的对象
          
          method1(new Worker());//非匿名的类 匿名的对象
          
          System.out.println("********************");
          
          //Person类是抽象类,不能造对象,但是下面的语句它new了,然后只能把Person类里的抽象方法重写一下
          //
          //创建了一匿名子类的对象:p
          Person p = new Person(){
  ​
              @Override
              public void eat() {
                  System.out.println("吃东西");
              }
  ​
              @Override
              public void breath() {
                  System.out.println("好好呼吸");
              }
              
          };
          
          method1(p);
          System.out.println("********************");
          //创建匿名子类的匿名对象
          method1(new Person(){
              @Override
              public void eat() {
                  System.out.println("吃好吃东西");
              }
  ​
              @Override
              public void breath() {
                  System.out.println("好好呼吸新鲜空气");
              }
          });
      }
      
      
      public static void method1(Person p){
          p.eat();
          p.breath();
      }
      
      public static void method(Student s){
          
      }
  }
  ​
  class Worker extends Person{
  ​
      @Override
      public void eat() {
      }
  ​
      @Override
      public void breath() {
      }
      
  }

多态的应用:模板方法设计模式(TemplateMethod)

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

解决的问题:

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

换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。

接口(inferface)

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。

  • 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有子父类的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。

    又如运动员类可以有篮球运动员子类和跨栏运动员子类,学生类可以有大学生子类和中学生子类,但是跨栏运动员和大学生可能都有跨栏的行为。

  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。

  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。

接口的使用

  1. 接口使用interface来定义

  2. Java中,接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前)。

  3. 如何定义接口:定义接口中的成员

  • JDK7及以前:只能定义全局常量和抽象方法

    全局常量:只能定义带有修饰符public static final的.但是书写时,可以省略不写

    抽象方法:只能定义带有修饰符public abstract的

public class InterfaceTest {
      public static void main(String[] args) {
          System.out.println(Flyable.MAX_SPEED);
          System.out.println(Flyable.MIN_SPEED);
  //final 用来修饰变量:此时的"变量"就称为是一个常量,不能进行赋值。
  //      Flyable.MIN_SPEED = 2;报错
          
          Plane plane = new Plane();
          plane.fly();
      }
  }
  interface Flyable{
      
      //全局常量
      public static final int MAX_SPEED = 7900;//第一宇宙速度
      int MIN_SPEED = 1;//省略了public static final
      
      //抽象方法
      public abstract void fly();
      void stop();//省略了public abstract
  }
  • JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)

  1. 接口中不能定义构造器,意味着接口不能够实例化。

  2. 在Java开发中,接口通过让类去实现(implements)的方式来使用,类实现接口:class Plane implements Flyable{}

  • 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化

  • 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类

  1. Java类可以实现多个接口 --->弥补了Java单继承性的局限性

    格式:class A extends B implements C,D,E

  2. 接口与接口之间可以继承,而且可以多继承

  3. 接口的具体使用,体现多态性

  4. 接口,实际上可以看做是一种规范

体现接口的多态性:

 public class USBTest {
      public static void main(String[] args) {
          
          Computer com = new Computer();
          Flash flash = new Flash();
          com.transferData(flash);
      }
  }
  ​
  class Computer{
      
      public void transferData(USB usb){
      //USB usb = new Flash();
          usb.start();
          
          System.out.println("具体传输数据的细节");
          
          usb.stop();
      }
  }
  interface USB{
      //常量:定义了长、宽、最大最小的传输速度等
      void start();
      void stop();
  }
  ​
  class Flash implements USB{
  ​
      @Override
      public void start() {
          System.out.println("U盘开启工作");
      }
  ​
      @Override
      public void stop() {
          System.out.println("U盘结束工作");
      }   
  }
  class Printer implements USB{
      @Override
      public void start() {
          System.out.println("打印机开启工作");
      }
      
      @Override
      public void stop() {
          System.out.println("打印机结束工作");
      }   
  }

创建接口匿名实现类的对象:

  public class USBTest {
      public static void main(String[] args) {
          
          Computer com = new Computer();
          //1.创建了接口的非匿名实现类的非匿名对象
          Flash flash = new Flash();
          com.transferData(flash);
          
          //2. 创建了接口的非匿名实现类的匿名对象
          com.transferData(new Printer());
          
          //3. 创建了接口的匿名实现类的非匿名对象
          USB phone = new USB(){
  ​
              @Override
              public void start() {
                  System.out.println("手机开始工作");
              }
  ​
              @Override
              public void stop() {
                  System.out.println("手机结束工作");
              }
              
          };
          com.transferData(phone);
          
          
          //4. 创建了接口的匿名实现类的匿名对象
          
          com.transferData(new USB(){
              @Override
              public void start() {
                  System.out.println("mp3开始工作");
              }
  ​
              @Override
              public void stop() {
                  System.out.println("mp3结束工作");
              }
          });
      }
  }
  ​
  class Computer{
      
      public void transferData(USB usb){//USB usb = new Flash();
          usb.start();
          
          System.out.println("具体传输数据的细节");
          
          usb.stop();
      }
      
      
  }
  ​
  interface USB{
      //常量:定义了长、宽、最大最小的传输速度等
      
      void start();
      
      void stop();
      
  }
  ​
  class Flash implements USB{
  ​
      @Override
      public void start() {
          System.out.println("U盘开启工作");
      }
  ​
      @Override
      public void stop() {
          System.out.println("U盘结束工作");
      }
      
  }
  ​
  class Printer implements USB{
      @Override
      public void start() {
          System.out.println("打印机开启工作");
      }
  ​
      @Override
      public void stop() {
          System.out.println("打印机结束工作");
      }
      
  }

接口的应用

  1. 接口的主要用途就是被实现类实现。(面向接口编程)

  2. 代理模式:

    概述: 代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

    明星A与明星A的经纪人

    明星A就是被代理类,明星A的经纪人就是代理类,

    明星A类有自己的方法,明星A的经纪人类有写明星A类的方法,

    本身明星要做的事,通过经纪人去完成,经纪人在做事的过程中涉及到的一些操作去找明星,就是经济人类执行方法的时候去调用明星类的方法。

    应用场景:

    • 安全代理:屏蔽对真实角色的直接访问。

    • 远程代理:通过代理类处理远程方法调用(RMI)

    • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象

    比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

    分类

    • 静态代理(静态定义代理类)

    • 动态代理(动态生成代理类)

    JDK自带的动态代理,需要反射等知识

  3. 工厂模式:

    概述:实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。其实设计模式和面向对象设计原则都是为了使得开发项目更加容易扩展和维护,解决方式就是一个“分工”。社会的发展也是这样,分工越来越细。原始社会的人:人什么都要会,自己种,自己打猎,自己织衣服,自己治病。现在社会的人:可以只会一样,其他都不会,只会Java 也能活,不会做饭,不会开车,不会....

接口和抽象类有什么联系和区别?

抽象类:

  • 一个类中有抽象方法,这个类就变成了抽象类。

  • 抽象类中class的前面必须有abstract修饰符。

  • 抽象类中可以有普通方法,也可以有抽象方法,而抽象方法的个数可以是0个,也可以是多个。

  • 子类继承父类,必须重写全部的抽象方法,除非这个类也变成了抽象类。

接口:

  • 表面上看,接口是一种特殊的抽象类,但是类是类,接口是接口,是并列的关系。

  • 接口中所有方法都必须是抽象的。(1.8之后允许接口定义非抽象方法)

  • 接口中方法定义默认为public abstract类型,成员变量默认为public static final 类型。(如果省略,系统会默认补全)。

抽象类和接口区别:

  • 相同点:抽象类和接口都不能被实例化

  • 一个类只能继承一个抽象类,而一个类可以实现多个接口。

  • 抽象类可以有构造方法,接口中不能有构造方法。

  • 抽象类中可以有成员变量,接口中没有成员变量。(被final修饰变成了常量)

  • 抽象类中可以有普通方法,接口中所有方法都必须是抽象的。(1.8后允许接口定义非抽象方法)

  • 抽象类中抽象方法的访问类型可以是public,protected,但接口中抽象方法的访问类型只能是public,并且默认为public abstract(省略则自动默认补全)。

  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;

  • 抽象类中可以有静态代码块和静态方法,接口中不能含有静态代码块以及静态方法

Java8接口新特性

JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法

知识点1:接口中定义的静态方法,只能通过接口来调用。

知识点2:通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法

知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则

知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。-->接口冲突。这就需要我们必须在实现类中重写此方法

知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法

类的成员之五:内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

现有一个Person类,里面有name,age属性,我们想在Person类中加一个brain(大脑)的类,因为把brain设置成属性太简单,所以我们就把它设置成内部类,Person是外部类,brain是内部类。

在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。

Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。

Inner class的名字不能与包含它的外部类类名相同;

  • 内部类分类:成员内部类( static成员内部类和非static成员内部类)局部内部类(方法内、代码块内、构造器内)、匿名内部类。

    • 成员内部类的特点

      一方面,作为外部类的成员:

      • 可以调用外部类的结构

      • 可以被static修饰

      • 可以被4种不同的权限修饰

      另一方面,作为一个类:

      • 类内可以定义属性、方法、构造器等

      • 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承

      • 可以被abstract修饰,因此可以被其他的颞部类继承

注意:

  1. 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。

  2. 外部类访问成员内部类的成员,需要“内部类.成员"或“内部类对象.成员”的方式

  3. 成员内部类可以直接使用外部类的所有成员,包括私有的数据

  4. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

关于内部类比较重要的问题

  1. 如何实例化成员内部类的对象

  2. 如何在成员内部类中区分调用外部类的结构

  3. 开发中局部内部类的使用(InnerClassTest1)

 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;
              }           
          };      
      }
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值