1.枚举
枚举是一种特殊的类,我们之前学的所有类都是继承自Object,但是枚举类继承自Enum类,
定义枚举
// >> TODO 使用enum而非class声明
public enum Category {
// >> TODO 必须在开始的时候以这种形式,创建所有的枚举对象,枚举的实例是在枚举创建的时候就定义好的,不允许在其他地方定义新的枚举类,所以,枚举的构造函数默认是Private的,写不写private都一样,如果写public就会报错、
FOOD(1),
// >> TODO 不可以重名
// FOOD(1),
COOK(3),//实例之间用逗号隔开
SNACK(5),
CLOTHES(7),
ELECTRIC(9);//末尾必须是分号
// 可以有属性
private int id;
// >> TODO 构造方法必须是private的,不写也是private的
Category(int id) {
this.id = id;
}
public int getId() {//定义方法和类方法是一样的
return id;
}
// public void setId(int id) {
// this.id = id;
// }
@Override
public String toString() {
return "Category{" +
"id=" + id +
'}';
}
}
枚举的使用:
public class UseEnum {
public static void main(String[] args) {
// >> TODO 获取所有枚举,看看枚举实例有哪些方法
for (Category category : Category.values()) {//可以通过点values的方法,获取枚举定义的实例
System.out.println("-----------" + category.getId() + "------------");//调用实例方法
System.out.println(category.ordinal());//实例属性
System.out.println(category.name());
System.out.println(category.toString());
}
System.out.println();
// >> TODO 根据名字获取枚举
System.out.println(Category.valueOf("FOOD"));
// System.out.println(Category.valueOf("food"));
Scanner in = new Scanner(System.in);
System.out.println("请输入枚举的名字:");
String categoryName = in.next();
Category enumInput = Category.valueOf(categoryName.trim().toUpperCase());
System.out.println("枚举的信息:" + enumInput.toString());
System.out.println("请输入要比较的枚举的名字:");
String categoryName2 = in.next();
Category enumInput2 = Category.valueOf(categoryName2.trim().toUpperCase());
System.out.println("第二次输入的枚举的信息:" + enumInput2.toString());
System.out.println(enumInput == enumInput2);
}
}
2.接口
// >> TODO 接口的定义使用interface,而非class
// >> TODO 接口中的方法,就是这个类型的规范,接口专注于规范,怎么实现这些规范,它不管
// >> TODO 接口无法被实例话,也就是不可以new一个接口的实例。
public interface ExpireDateMerchandise {
// >> TODO 接口里的方法都是public abstract修饰的,方法有名字,参数和返回值,没有方法体,以分号;结束,
// TODO 接口注释最好写一下
boolean notExpireInDays(int days);
// >> TODO 因为接口里的方法都是且只能用public abstract修饰,所以这俩修饰符可以省略
// >> TODO abstract就是抽象方法的修饰符,没有方法体,以分号结束
Date getProducedDate();
public abstract Date getExpireDate();
double leftDatePercentage();
double actualValueNow(double leftDatePercentage);
// >> TODO 接口里不能定义局部变量,定义的变量默认都是public static final的,这三个修饰符同样可以省略
public static final int VAL_IN_INTERFACE = 999;
}
// >> TODO 接口甚至可以没有任何方法的定义,只是规定一种类型
public interface VirtualMerchandise {
}
``
```java
在这里插入代码片
public interface Intf1 {
void m1();
}
public interface Intf2 {
void m1();
void m2();
}
package com.geekbang.intf;
// >> TODO 接口也可以继承接口。接口可以继承多个接口,接口之间的继承要用extends
// >> TODO 接口不可以继承类
// >> TODO 继承的接口,可以有重复的方法,但是签名相同时,返回值必须完全一样,否则会有编译错误
public interface Intf3 extends Intf1, Intf2{
void m3();
}
接口的使用:
public class INterface implements ExpireDateMerchandise
TODO 可以用实现接口的类的引用,给接口的引用赋值。类似于可以使用子类的引用给父类赋值
INterface iNterface=new INterface();
ExpireDateMerchandise expireDateMerchandise1=iNterface;
接口也可以有方法体:
// >> TODO 缺省的实现方法,用default修饰,可以有方法体
public interface ExpireDateMerchandise {
// >> TODO 缺省的实现方法,用default修饰,可以有方法体
default boolean notExpireInDays(int days) {
return daysBeforeExpire() > days;
}
Date getProducedDate();
public abstract Date getExpireDate();
default double leftDatePercentage() {
return 1.0 * daysBeforeExpire() / (daysBeforeExpire() + daysAfterProduce());
}
double actualValueNow(double leftDatePercentage);
// >> TODO 接口中可以有私有方法,不需要用default修饰
// >> TODO 接口里的私有方法,可以认为是代码直接插入到使用的地方
private long daysBeforeExpire() {
long expireMS = getExpireDate().getTime();
long left = expireMS - System.currentTimeMillis();
if (left < 0) {
return -1;
}
// 返回值是long,是根据left的类型决定的
return left / (24 * 3600 * 1000);
}
private long daysAfterProduce() {
long produceMS = getProducedDate().getTime();
long left = System.currentTimeMillis() - produceMS;
if (left < 0) {
return -1;
}
// 返回值是long,是根据left的类型决定的
return left / (24 * 3600 * 1000);
}
// >> TODO 有方法的接口,并不是多继承。接口不可以继承类,这一点就打破了
// >> TODO 同样,接口不可以声明实例变量。其方法是有限制的,比如这个接口,因为不能声明实例变量,
// >> TODO 只能通过getProducedDate和getExpireDate,间接通过实现接口的类,获取数据
// >> TODO 和抽象方法不同,如果一个类实现了两个接口,并且两个接口里有相同的缺省方法,编译器会报错
public interface ExpireDateMerchandise {
// >> TODO 缺省方法,也有this自引用,但是只能调用接口里的方法,或者继承的接口里的方法
// >> TODO 因为能new出实例来的,肯定是实现了所有方法的,this自引用就是指向那个对象,所以使用起来不会有问题
// >> TODO 接口中可以有静态方法,不需要用default修饰。静态方法可以被实现接口的类继承
public static long daysBetween(long from, long to) {
long gap = to - from;
if (gap < 0) {
return -1;
}
return gap / (24 * 3600 * 1000);
}
3.抽象类:接口和类的混合体
// >> TODO 抽象类用abstract修饰,抽象类可以继承别的类或者抽象类,也可以实现接口
// >> TODO 抽象类可以有抽象方法,抽象方法可以来自实现的接口,也可以自己定义
// >> TODO 抽象类不可以被实例化
// >> TODO 抽象类也可以没有抽象方法,没有抽象方法的抽象类,也不可以被实例化。
// >> TODO 简单来说,抽象类就两点特殊:1)被abstract修饰,可以有抽象方法 2)不可以被实例化
public abstract class AbstractExpireDateMerchandise extends MerchandiseV2 implements ExpireDateMerchandise {
private Date produceDate;
private Date expirationDate;
// >> TODO 抽象类里构造方法的语法和类一样。
public AbstractExpireDateMerchandise(String name, String id, int count, double soldPrice, double purchasePrice, Date produceDate, Date expirationDate) {
super(name, id, count, soldPrice, purchasePrice);
this.produceDate = produceDate;
this.expirationDate = expirationDate;
}
public AbstractExpireDateMerchandise(String name, String id, int count, double soldPrice, Date produceDate, Date expirationDate) {
super(name, id, count, soldPrice);
this.produceDate = produceDate;
this.expirationDate = expirationDate;
}
public AbstractExpireDateMerchandise(Date produceDate, Date expirationDate) {
this.produceDate = produceDate;
this.expirationDate = expirationDate;
}
// >> TODO @ 是Java中的注解(annotation),后面我们会详细讲述
// >> TODO @Override代表此方法覆盖了父类的方法/实现了继承的接口的方法,否则会报错
public boolean notExpireInDays(int days) {
return daysBeforeExpire() > 0;
}
public Date getProducedDate() {
return produceDate;
}
public Date getExpireDate() {
return expirationDate;
}
public double leftDatePercentage() {
return 1.0 * daysBeforeExpire() / (daysBeforeExpire() + daysAfterProduce());
}
// @Override
// public double actualValueNow(double leftDatePercentage) {
// return 0;
// }
// >> TODO 抽象类里自己定义的抽象方法,可以是protected,也可以是缺省的,这点和接口不一样
// protected abstract void test();
// TODO 这俩方法是私有的,返回值以后即使改成int,也没有顾忌
private long daysBeforeExpire() {
long expireMS = expirationDate.getTime();
long left = expireMS - System.currentTimeMillis();
if (left < 0) {
return -1;
}
// 返回值是long,是根据left的类型决定的
return left / (24 * 3600 * 1000);
}
private long daysAfterProduce() {
long produceMS = produceDate.getTime();
long past = System.currentTimeMillis() - produceMS;
if (past < 0) {
// 生产日期是未来的一个时间?315电话赶紧打起来。
return -1;
}
// 返回值是long,是根据left的类型决定的
return past / (24 * 3600 * 1000);
}
}
抽象类可以被继承,继承他的可以是具体的类,也可以是抽象类。
匿名类
// >> TODO 对于抽象类,也可以给构造方法传递参数
private UnitSpecAbs anywhereAbs = new UnitSpecAbs(1.2, "default") {
@Override
public double getNumSpec() {
return Math.max(Phone.this.speed, this.getSpec());
}
this.screenSize = screenSize;
// >> TODO 可以像平常的类一样使用局部内部类
this.speed = cpuHZ;
this.cpu = new UnitSpec() {
@Override
public double getNumSpec() {
// >> TODO 实际用的比较多的是匿名类和静态内部类(为了单例),成员内部类和局部内部类用的比较少
// >> TODO 同样的,方法里的匿名类在访问局部变量和参数时,它们也必须是实际final的
return Math.max(Phone.this.speed, Math.max(cpuHZ, localCPUHZ));
}