Java基础加强重温_02:接口(成分:抽象方法、常量、默认方法、静态方法、私有方法,实现、继承)、final关键字(修饰类、方法)、单例设计模式(懒汉、饿汉)、枚举(底层原理、编译与反编译)

一、接口

接口是更加彻底的抽象,接口成分仅有抽象方法、常量。(JDK1.8之前),接口同样是不能创建对象的。

从jdk1.9开始:接口中允许定义私有方法

定义格式
修饰符 interface 接口名称{
  // 抽象方法
  // 常量
}

1、接口成分

抽象方法

接口定义的方法全部都是抽象方法,会自动省略 public abstract

// 接口抽象方法
// public abstract void run();
void run();

常量

接口定义的变量是常量,会自动省略 public final static。也就是说在接口中定义的成员变量实际上是一个常量。
public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。
常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接。

  // public static final int AGE = 12 ;
  int AGE  = 12;  //常量
  
  // public static final String SCHOOL_NAME = "黑马程序员"; 
  String SCHOOL_NAME = "黑马程序员"; // 字符串常量

2、接口的实现和继承

实现

类与接口的关系为实现关系,即类实现接口,实现使用 implements 关键字。 该类可以称为接口的实现类,也可以称为接口的子类。

Java中的类可以实现一个接口也可以同时实现多个接口,可以理解成实现多个规范。
实现的多个接口中的抽象方法全部需要重写,没有全部重写抽象方法的实现类也变成抽象类。

class 实现类 implements 接口1,接口2,...{

}

继承

接口和接口之间可以继承,可以继承一个/多个(但继承/多几次)。
接口继承接口,就是把其他接口的抽象方法与本接口进行了合并。

  • 1、提高代码的复用性
  • 2、提高代码的拓展性
interface 子接口 extends 父接口1,父接口2,...{

}

类与接口是实现关系
接口与接口是继承关系

3、JDK1.8以后接口新增特性

含默认方法和静态方法(>=JDK1.8)

默认方法

使用 default 修饰,不可省略,供子类调用或者子类重写(子类对象.默认方法)。
权限修饰符默认是 public(默认加上),可以被继承、被重写,但是不能像子类一样使用super调用。

静态方法

使用 static 修饰,供接口直接调用(接口名.静态方法)。

  • 继承、实现都无法重写静态方法
  • 静态方法可以被继承,但是不能通过实现继承
    理解: 类1 extends 类2 implements 接口,类1能拿到类2的静态方法,但不能拿到接口的静态方法。
    接口1 extends 接口2,接口1可以拿到接口2的静态方法
public interface InterFaceName {
	// 默认方法
	public default void method() {
		// 执行语句
	}
	
	// 静态方法
	public static void method2() {
		// 执行语句  
	}
}
重写默认方法注意(了解):

子接口重写默认方法时,default关键字可以保留。
实现类重写默认方法时,default关键字不可以保留。

含私有方法和私有静态方法(>=JDK1.9)

私有方法: 使用 private 修饰,供接口中的默认方法或者静态方法调用。

  • 接口定义公开的默认方法和公开的静态方法调用接口的私有方法

定义格式

private [static] 返回值 方法名(参数...) {
}

代码示例:

public interface InterFaceName {
	//私用方法
	private void method() {
		// 执行语句
	}

	//私有静态方法
	private static void method() {
		// 执行语句
	}
}

普通方法与静态方法间的调用

  • 静态方法能调用静态方法
  • 普通方法能调用普通方法和静态方法

4、类实现多个接口注意事项

接口多实现情况下各个接口的静态方法调用
  • 调用方式:
    各自接口名.静态方法
  • 静态方法名一致不影响,可以通过接口名区分
父类中的方法和接口中的默认方法重名问题

方法优先级,就近原则:本类 -> 父类 -> 接口

当多实现情况下,接口中有同名的default方法时,会报错(二义性)

二、final关键字

final是不可改变,最终的含义。可以用于修饰类、方法和变量(成员变量、静态成员变量、局部变量)。

  • 类:被修饰的类,不能被继承。
  • 方法:被修饰的方法,不能被重写。
  • 变量:被修饰的变量,有且仅能被赋值一次。(在声明变量的时候必须同事赋值)

1、修饰类

被final修饰的类无法被继承(太监类)

final class 类名 {
}

查询API发现像 public final class String 、 public final class Math 、 public final class Scanner等,很多学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。

2、修饰方法

final修饰的方法无法被子类重写。

修饰符 final 返回值类型 方法名(参数列表){
  //方法体
}

代码:

class Fu2 {
	//void(返回值)前面的修饰符可以随意调换顺序
	final public void show1() {
		System.out.println("Fu2 show1");
	}

	public void show2() {
		System.out.println("Fu2 show2");
	}
}

class Zi2 extends Fu2 {
// @Override
// public void show1() {
	// System.out.println("Zi2 show1");
// }

	@Override
	public void show2() {
		System.out.println("Zi2 show2");
	}

3、修饰变量(局部变量、静态成员变量、成员变量)

局部变量

局部变量:在方法中定义的变量。
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。

 public class FinalDemo1 {
  public static void main(String[] args) {
    // 声明变量,使用final修饰
    final int a;  //报错
    // 第一次赋值
    a = 10;
    // 第二次赋值
    a = 20; // 报错,不可重新赋值
    
    // 声明变量,直接赋值,使用final修饰
    final int b = 10;
    // 第二次赋值
    b = 20; // 报错,不可重新赋值
 }
}

final修饰 静态成员变量

static修饰的变量,再被final修饰称之为【常量】。 即:final static 变量名 = 变量值;

  • static修饰变量在声明是可以不初始化(赋值),final修饰只能被赋值一次且在声明时必须初始化(赋值)

被final修饰的常量名称,一般都有书写规范,所有字母都大写。

class Video {
    //等价静态的基本类型变量
    //常量有一个命名规范:常量名一般用大写
    //快捷键 :ctrl+shift+u,选择部分变大写
    //直接通过类名.LEVEL调用,不需要经过构造方法,必须在声明的时候进行初始化
    
    public static final  char LEVEL = 3;

}

final修饰 实例成员变量

实例成员变量涉及到初始化的问题,初始化方式有显示初始化和构造器初始化,只能选择其中一个方式初始化

  • 显示初始化: 在声明成员变量的时候立即赋值
public class Student {
  final int num = 10;
}
  • 构造器初始化: 在构造方法中赋值一次。
    每个构造方法中(有参/无参)都要赋值一次
public class Student {
	final int num = 10;
	final int num2;
  	
  	//在无参中赋值
	public Student() {
		this.num2 = 20;
	//  this.num2 = 20;
	}
 
 	//在有参中赋值
	public Student(String name) {
		this.num2 = 20;
	//	this.num2 = 20;
	}
}

三、单例设计模式

单例设计模式,即一个类只有一个对象实例。正常情况下一个类可以创建无数个对象,在单例模式中一个类只能创建一个对象

单例设计模式的概念/作用:

使得JAVA程序的某个类在运行阶段只能创建一个对象。

实现步骤

  • 1、私有构造方法,private修饰构造方法
  • 2、定义私有静态成员变量(常量)存储单例
  • 3、通过公开的静态getter方法获取单例

单例设计模式的分类

1.饿汉式单例案例

饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。

public class Singleton {
  // 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  private Singleton() {}
  
  // 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
  private static final Singleton instance = new Singleton();
 
  // 3.定义一个静态方法返回这个唯一对象。
  public static Singleton getInstance() {
    return instance;
 }
}

获取单例

Singleton sing = Singleton.getInstance();

2.懒汉式单例

懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才例化出对象。不着急,故称为“懒汉模式”。

public class Singleton {
  // 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
  private static Singleton instance;
 
  // 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  private Singleton() {}
 
  // 3.定义一个静态方法返回这个唯一对象。要用的时候才例化出对象
  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton();
   }
    return instance;
 }
}

3、双重锁形式(解决懒汉式的线程安全问题)

懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态。我们在学习完多线程的时候还会再讲解如何解决这个问题。

饿汉式和懒汉式的区别

静态成员变量是声明的时候初始化,还是get的时候初始化

饿汉式还是懒汉式用的多呢?

饿汉式实现更简单,懒汉式其实有线程安全问题(学多线程的时候会学)。

四、多例设计模式(与单例设计模式对应)

1、多例设计模式

https://blog.csdn.net/qq_38737992/article/details/89644649

2、老王讲设计模式(四)——多例模式

https://www.jianshu.com/p/78061e1f79a5

五、枚举

枚举是一个特殊的类,有固定实例个数的类型,我们可以把枚举理解成有固定个数实例的多例模式。即枚举能用来表达固定的几个特定的选项。

枚举定义格式:

public enum 枚举名{
	//第一行都是罗列枚举实例,这些枚举实例直接写大写名字即可。
	选项1,选项2,....;
}

枚举的使用:

作为数据类型使用

枚举名.选项

1、不使用枚举存在的问题

假设我们要定义一个人类,人类中包含姓名和性别。通常会将性别定义成字符串类型,代码演示如下:

public class Person {
  private String name;
  private String sex;
  
  public Person() {
 }
 
  public Person(String name, String sex) {
    this.name = name;
    this.sex = sex;
 }
   // 省略get/set/toString方法
}
public class Demo01 {
  public static void main(String[] args) {
    Person p1 = new Person("张三", "男");
    Person p2 = new Person("张三", "abc"); // 因为性别是字符串,所以我们可以传入任意字符串
 }
}

不使用枚举存在的问题:可以给性别传入任意的字符串,导致性别是非法的数据,不安全,违反代码的逻辑性。

2、枚举的作用与应用场景

枚举的作用:一个方法接收的参数是固定范围之内的时候,即可以穷举的固定几个选项,那么可使用枚举。
枚举的应用:枚举通常可以用于做信息的分类,如性别,方向,季度等。

枚举的最大的意义在于,提高代码的可读性。

入门案例

1、 定义枚举:BOY表示男,GIRL表示女

enum Sex {
  BOY, GIRL; // 男,女
}

2、Perosn中的性别有String类型改为Sex枚举类型

public class Person {
  private String name;
  private Sex sex;
  public Person() {
 }
 
  public Person(String name, Sex sex) {
    this.name = name;
    this.sex = sex;
 }
  // 省略get/set/toString方法
}

3.、使用是只能传入枚举中的固定值

public class Demo02 {
  public static void main(String[] args) {
    Person p1 = new Person("张三", Sex.BOY);
    Person p2 = new Person("张三", Sex.GIRL);
    Person p3 = new Person("张三", "abc");
 }
}

测试运行。

入门案例枚举底层分析

枚举的本质是一个类,我们刚才定义的Sex枚举最终效果如下(通过编译-反编译后分析):

enum Sex {
  BOY, GIRL; // 男,女
}

//1、枚举Sex是一个final clss 继承了Enum类
final class Sex extends Enum{

  //2、枚举中的选项其实是枚举类的常量,用枚举类做数据类型,在静态代码块中初始化
  public static final Sex BOY;
  public static final Sex GIRL;
  
  public static SEX[] values(){};
  public static SEX valueOf(java.lang.String){};
  
  //3、枚举中的构造方法是私有的,多例(单例)
  private Sex(){
  
  }
  
  //初始化枚举选项
  static {
  	BOY = new SEX();
  	GIRL =  new SEX();
  };
}

因为枚举的本质是一个类,所以枚举中还可以有成员变量,成员方法等。

public enum Sex {
	BOY(18), GIRL(16);
  
	//成员变量
	public int age;
  
	// getter/setter方法
	Sex(int age) {
		this.age = age;
	}
	
	//成员方法
	public void showAge() {
		System.out.println("年龄是: " + age);
	}
}

测试类

public class Demo03 {
  public static void main(String[] args) {
    Person p1 = new Person("张三", Sex.BOY);
    Person p2 = new Person("张三", Sex.GIRL);
    Sex.BOY.showAge();
    Sex.GIRL.showAge();
 }
}
编译和反编译研究枚举底层

枚举是一种特殊的类

编译:

  • java文件 -> javac编译 -> class文件

反编译工具:

  • class文件 -> 反编译工具 -> java文件

怎么找class文件:

  • 模块名右键->show in explorer -> out -> …->包名->class文件
  • 如果没有:要编译的类右键->recompile

反编译工具:

  • jd-gui、xjad:配合起来用

小结:

枚举的底层
1、枚举是一个final clss 继承了 Enum类
2、枚举中的选项其实是枚举类的常量,在静态代码块中初始化
3、枚举中的构造方法是私有的,多例(单例)。一个选项单例,多个选项多例
4、枚举也能定义方法、构造方法

枚举如果想携带额外信息(改造枚举类Sex):
  • 1、在枚举中定义新的成员变量
enum Sex {
	BOY, GIRL; // 男,女
	
	//如果想枚举额外添加更多的信息
	//1、在枚举中定义新的成员变量
    private String name;

}

  • 2、修改构造方法,把新的成员变量加入
enum Sex {
	BOY, GIRL; // 男,女
	
	//如果想枚举额外添加更多的信息
	//1、在枚举中定义新的成员变量
    private String name;

	// 2、修改构造方法,把新的成员变量加入
    private Direction(String name) {
        this.name = name;
    }

	//3、getter/setter
}
  • 3、枚举中常量需要根据新的构造方法进行调整
enum Sex {
	//3、枚举中常量需要根据新的构造方法进行调整,传入参数。
	BOY("男"), GIRL("女"); // 男,女
	
	//如果想枚举额外添加更多的信息
	//1、在枚举中定义新的成员变量
    private String name;

	// 2、修改构造方法,把新的成员变量加入
    private Direction(String name) {
        this.name = name;
    }

}

这时候的底层,定义新成员变量后反编译,初始化选项的static代码块

  //初始化枚举选项
static {
  BOY = new SEX("BOY""男");
  GIRL = new SEX("GIRL""女");
};
  • 4、通过成员变量对应的getter/setter方法可以获得枚举选项携带的额外信息
    BOY.getName(),GIRL.getName()

理解:即定义新的变量,是给选项定义的。选项要传入这个参数。static代码块初始化后选项会携带这个参数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值