JavaSE部分——面向对象(下)
面向对象(下)
一、Java 8 增强的包装类
为了解决8种基本数据类型的变量不能当成Object类型变量使用的问题,Java提供了包装类(Wrapper Class)的概念,为8种基本数据类型分别定义了相应的引用类型,并称之为基本数据类型的包装类。
- 自动装箱:把一个基本类型变量直接赋给对应的包装类变量,或者赋给Object变量(Object是所有类的父类,子类对象可以直接赋给父类变量)
- 自动拆箱:允许直接把包装类对象直接赋给一个对应的基本类型变量
把字符串类型的值转换为基本类型的值有两种方式
- 利用包装类提供的parseXxx(String s)静态方法(除 Character 之外的所有包装类都提供了该方法)
- 利用包装类提供的valueOf(String s)静态方法
- String类也提供了多个重载valueOf()方法,用于将基本类型变量转换成字符串
代码示例
public class Primitive2String {
public static void main(String[] args) {
String intStr = "123";
// 把一个特定字符串转换成int变量
int it1 = Integer.parseInt(intStr);
int it2 = Integer.valueOf(intStr);
System.out.println(it2);
String floatStr = "4.56";
// 把一个特定字符串转换成float变量
float ft1 = Float.parseFloat(floatStr);
float ft2 = Float.valueOf(floatStr);
System.out.println(ft2);
// 把一个float变量转换成String变量
String ftStr = String.valueOf(2.345f);
System.out.println(ftStr);
// 把一个double变量转换成String变量
String dbStr = String.valueOf(2.344);
System.out.println(dbStr);
// 把一个boolean变量转换成String变量
String boolStr = String.valueOf(true);
System.out.println(boolStr);
}
}
基本类型变量和""进行连接运算,系统会自动把基本类型变量转换成字符串
//intStr的值为"5" String intStr = 5 + "";
比较两个基本类型值的大小——静态的compare(xxx val1,xxx val2)
- static String toUnsignedString(int/long i):int或long型整数转换为无符号整数对应的字符串
- static String toUnsignedString(int/long i,int radix):int或long型整数转换为指定进制的无符号整数对应的字符串
- static xxx oarseUnsignedXxx(String s):指定字符串解析成无符号整数
- static xxx parseUnsignedXxx(String s,int radix):指定字符串按指定进制解析成无符号整数
- static int compareUnsigned(xxx x,xxx y):将x、y两个整数转换为无符号整数后比较大小
- static long divideUnsigned(long dividend,long divisor):将x、y两个整数转换为无符号整数后计算他们相除的商
- static long remainderUnsigned(long dividend,long divisor):将x、y两个整数转换为无符号整数后计算他们相除的余数
二、处理对象
1、打印对象和toString方法
- Object类提供的toString()方法总是返回该对象实现类的"类名+@+hashCode"值
重写Object类的toString()方法
class Apple {
private String color;
private double weight;
public Apple() {
}
// 提供有参数的构造器
public Apple(String color, double weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
// 重写toString()方法,用于实现Apple对象的“自我描述”
public String toString() {
return "一个苹果,颜色是: " + color + ",重量是: " + weight;
}
}
public class ToStringTest {
public static void main(String[] args) {
Apple a = new Apple("红色", 5.68);
// 打印Apple对象
System.out.println(a);
}
}2、==和equals方法
判断两个变量是否相等有两种方式
- ==运算符:如果两个变量是基本类型变量,且都是数据类型,则只要两个变量的值相等,就将返回true;对于两个引用类型变量,只有它们指向同一个对象时,==判断才会返回true,==不可用于比较类型上没有父子关系的两个对象
- equals()方法:Object默认提供的equals()只是比较对象的地址
重写equals()方法应满足下列条件:- 自反性:对任意x,x.equals(x)一定返回true
- 对称性:对任意x和y,如果y.equals(x)返回true,则x.equals(y)也返回true
- 传递性:对任意x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)一定返回true
- 一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应该保持一致,要么一直true,要么一直false
- 对任何不是null的x,x.equals(null)一定返回false
三、类成员
1、理解类成员
2、单例(Singleton)类
如果一个类始终只能创建一个实例,则这个类被称为单例类。
代码示例
class Singleton {
// 使用一个类变量来缓存曾经创建的实例
private static Singleton instance;
// 对构造器使用private修饰,隐藏该构造器
private Singleton() {
}
// 提供一个静态方法,用于返回Singleton实例
// 该方法可以加入自定义控制,保证只产生一个Singleton对象
public static Singleton getInstance() {
// 如果instance为null,则表明还不曾创建Singleton对象
// 如果instance不为null,则表明已经创建了Singleton对象
// 将不会重新创建新的实例
if (instance == null) {
// 创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
}
return instance;
}
}
public class SingletonTest {
public static void main(String[] args) {
// 创建Singleton对象不能通过构造器
// 只能通过getInstance方法来得到实例
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);// 将输出true
}
}四、final修饰符
final关键字可用于修饰类、变量和方法
- 修饰变量时,表示该变量一旦获得了初始值就不可被改变
1、final成员变量
- final修饰的成员变量必须由程序员显示地指定初始值
- 类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定
- 实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定
2、final局部变量
public class FinalLocalVariableTest {
public void test(final int a) {
// 不能对final修饰的形参赋值,下面语句非法
// a=5;
}
public static void main(String[] args) {
// 定义final局部变量时指定默认值,则str变量无法重新复制
final String str = "hello";
// 下面赋值语句非法
// str = "Java";
// 定义final局部变量时没有指定默认值,则d变量可被赋值一次
final double d;
// 第一次赋初始值,成功
d = 5.6;
// 对final变量重复赋值,下面语句非法
// d = 3.4;
}
}3、final修饰基本类型变量和引用类型变量的区别
- 当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。
- 对于引用类型变量而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变
代码示例
import java.util.Arrays;
class Person {
private int age;
public Person() {
}
// 有参数的构造器
public Person(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class FinalReferenceTest {
public static void main(String[] args) {
// final修饰数组变量,iArr是一个引用变量
final int[] iArr = { 5, 6, 12, 9 };
System.out.println(Arrays.toString(iArr));
// 对数组元素进行排序,合法
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
// 对数组元素赋值,合法
iArr[2] = -8;
System.out.println(Arrays.toString(iArr));
// 下面语句对iArr重新赋值,非法
// iArr = null;
// final修饰Person变量,p是一个引用变量
final Person p = new Person(45);
// 改变Person对象的age实例变量,合法
p.setAge(23);
System.out.println(p.getAge());
// 下面语句对p重新赋值,非法
// p = null;
}
}4、final方法
- final修饰的方法不可被重写
5、final类
- final修饰的类不可以有子类
6、不可变类
- 创建该类的实例后,该实例的实例变量是不可改变的
- 创建自定义的不可变类:
- 使用private和final修饰符来修饰该类的成员变量
- 提供带参数构造器,用于根据传入参数来初始化类的成员变量
- 仅为该类的成员变量提供getter方法,不要为该类的成员变量提供setter方法
- 如果有必要,重写Object类的hashCode()和equals()方法。equals()方法根据关键成员变量来作为两个对象是否相等的标砖,除此之外,还应该保证两个equals()方法判断为相等的对象的hashCode()也相等。
代码示例
public class Address {
private final String detail;
private final String postCode;
// 在构造器里初始化两个实例变量
public Address() {
this.detail = "";
this.postCode = "";
}
public Address(String detail, String postCode) {
this.detail = detail;
this.postCode = postCode;
}
// 仅为两个实例变量提供getter方法
public String getDetail() {
return detail;
}
public String getPostCode() {
return postCode;
}
// 重写equals()方法,判断两个对象是否相等
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj.getClass() == Address.class) {
Address ad = (Address) obj;
// 当detail和postCode相等时,可认为两个Address对象相等
if (this.getDetail().equals(ad.getDetail())
&& this.getPostCode().equals(ad.getPostCode())) {
return true;
}
}
return false;
}
public int hashCode() {
return detail.hashCode() + postCode.hashCode() * 31;
}
}7、缓存实例的不可变类
- 如果程序经常需要使用相同的不可变类实例,则应该考虑缓存这种不可变类的实例
使用数组作为缓存池,实现一个缓存实例的不可变类
class ChacheImmutale {
private static int MAX_SIZE = 10;
// 使用数组来缓存已有的实例
private static ChacheImmutale[] cache = new ChacheImmutale[MAX_SIZE];
// 记录缓存实例在缓存中的位置,cache[pos-1]是最新缓存的实例
private static int pos = 0;
private final String name;
private ChacheImmutale(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static ChacheImmutale valueOf(String name) {
// 遍历已缓存的对象
for (int i = 0; i < MAX_SIZE; i++) {
// 如果已有相同实例,则直接返回该缓存的实例
if (cache[i] != null && cache[i].getName().equals(name)) {
return cache[i];
}
}
// 如果缓存池已满
if (pos == MAX_SIZE) {
// 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置
cache[0] = new ChacheImmutale(name);
// 把pos设为1
pos = 1;
} else {
// 把新创建的对象缓存起来,pos加1
cache[pos++] = new ChacheImmutale(name);
}
return cache[pos - 1];
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj.getClass() == ChacheImmutale.class) {
ChacheImmutale ci = (ChacheImmutale) obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode() {
return name.hashCode();
}
}
public class ChacheImmutaleTest {
public static void main(String[] args) {
ChacheImmutale c1 = ChacheImmutale.valueOf("hello");
ChacheImmutale c2 = ChacheImmutale.valueOf("hello");
// 下面代码将输出true
System.out.println(c1 == c2);
}
}Integer类构造器和valueOf()方法存在的差异
public class IntegerTest {
public static void main(String[] args) {
// 生成新的Integer对象
Integer in1 = new Integer(6);
// 生成新的Integer对象,并缓存该对象
Integer in2 = Integer.valueOf(6);
// 直接从缓存中取出Integer对象
Integer in3 = Integer.valueOf(6);
System.out.println(in1 == in2);// 输出false
System.out.println(in2 == in3);// 输出true
// 由于Integer只缓存-128~127之间的值
// 因此200对应的Integer对象没有被缓存
Integer in4 = Integer.valueOf(200);
Integer in5 = Integer.valueOf(200);
System.out.println(in4 == in5);// 输出false
}
}五、抽象类
1、抽象方法和抽象类
- 抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法
- 抽象方法和抽象类的规则:
- 抽象方法和抽象类必须使用abstract修饰符来定义,抽象方法不能有方法体
- 抽象类不能被实例化
- 抽象类可以包含成员变量、方法(普通方法和抽象方法)、构造器、初始化块、内部类(接口、枚举)5种成分
- 含有抽象方法的类只能被定义成抽象类
Shape抽象类
public abstract class Shape {
{
System.out.println("执行Shape的初始化块...");
}
private String color;
// 定义一个计算周长的抽象方法
public abstract double calPerimeter();
// 定义一个返回形状的抽象方法
public abstract String getType();
// 定义Shape的构造器,该构造器并不是用于创建Shape对象
// 而是用于被子类调用
public Shape() {
}
public Shape(String color) {
System.out.println("执行Shape的构造器...");
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}定义一个三角形类
public class Triangle extends Shape {
// 定义三角形的三边
private double a;
private double b;
private double c;
public Triangle(String color, double a, double b, double c) {
super(color);
setSides(a, b, c);
}
public void setSides(double a, double b, double c) {
if (a >= b + c || b >= a + c || c >= a + b) {
System.out.println("三角形两边之和必须大于第三边");
return;
}
this.a = a;
this.b = b;
this.c = c;
}
// 重写Shape类的计算周长的抽象方法
public double calPerimeter() {
return a + b + c;
}
// 重写Shape类的返回形状的抽象方法
public String getType() {
return "三角形";
}
}定义一个Circle普通类
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
// 重写Shape类的计算周长的抽象方法
public double calPerimeter() {
return 2 * Math.PI * radius;
}
// 重写Shape类的返回形状的抽象方法
public String getType() {
return getColor() + "圆形";
}
public static void main(String[] args) {
Shape s1 = new Triangle("黑色", 3, 4, 5);
Shape s2 = new Circle("黄色", 3);
System.out.println(s1.getType());
System.out.println(s1.calPerimeter());
System.out.println(s2.getType());
System.out.println(s2.calPerimeter());
}
}注意:abstract不能用于修饰成员变量和局部变量,也不能用于修饰构造器。static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法,但它们可以同时修饰内部类。abstract修饰的方法必须被其子类重写,否则这个方法将永远不会有方法体,因此abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法。
2、抽象类的作用
六、Java 9 改进的接口
1、接口的概念
- 接口定义的是多个共同的公共行为规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公用方法
2、Java 9 中接口的定义
[修饰符] interface 接口名 extends 父接口1,父接口2...
{
零个到多个常量定义...
零个到多个抽象方法定义...
零个到多个内部类、接口、枚举定义...
零个到多个私有方法、默认方法或类方法定义...
}接口里不能包含构造器和初始化块定义
接口里定义成员变量只能在定义时指定默认值,public static final修饰符可省略
public interface Output { // 接口里定义的成员变量只能是常量 int MAX_CACHE_LINE = 50; // 接口里定义的普通方法只能是public的抽象方法 void out(); void getData(String msg); // 在接口中定义默认方法,需要使用default修饰 default void print(String... msgs) { for (String msg : msgs) { System.out.println(msg); } } // 在接口中定义默认方法,需要使用default修饰 default void test() { System.out.println("默认的test()方法"); } // 在接口中定义类方法,需要使用static修饰 static String staticTest() { return "接口里的类方法"; } // 定义私有方法 private void foo() { System.out.println("foo 私有方法"); } // 定义私有静态方法 private static void bar() { System.out.println("bar 私有静态方法"); } }
3、接口的继承
interface InterfaceA {
int PROP_A = 5;
void testA();
}
interface InterfaceB {
int PROP_B = 6;
void testB();
}
interface InterfaceC extends InterfaceA, InterfaceB {
int PROP_C = 7;
void testC();
}
public class InterfaceExtendsTest {
public static void main(String[] args) {
System.out.println(InterfaceC.PROP_A);
System.out.println(InterfaceC.PROP_B);
System.out.println(InterfaceC.PROP_C);
}
}4、使用接口
[修饰符] class 类名 extends 父类 implements 接口1,接口2...
{
类体部分
}代码示例
//定义一个Product接口
interface Product {
int getProduceTime();
}
//让Printer类实现Output和Product接口
public class Printer implements Output, Product {
private String[] printData = new String[MAX_CACHE_LINE];
// 用以记录当前需打印的作业数
private int dataNum = 0;
public void out() {
// 只要还有作业,就继续打印
while (dataNum > 0) {
System.out.println("打印机打印: " + printData[0]);
// 把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
public void getData(String msg) {
if (dataNum >= MAX_CACHE_LINE) {
System.out.println("输出队列已满,添加失败");
} else {
// 把打印数据添加到队列里,已保存数据的数量加1
printData[dataNum++] = msg;
}
}
public int getProduceTime() {
return 45;
}
public static void main(String[] args) {
// 创建一个Printer对象,当成Output使用
Output o = new Printer();
o.getData("轻量级Java EE企业应用实战");
o.getData("疯狂Java讲义");
o.out();
o.getData("疯狂Android讲义");
o.getData("疯狂Ajax讲义");
o.out();
// 调用Output接口中定义的默认方法
o.print("孙悟空", "猪八戒", "白骨精");
o.test();
// 闯将一个Printer对象,当成Product使用
Product p = new Printer();
System.out.println(p.getProduceTime());
// 所有接口类型的引用变量都可直接赋给Object类型的变量
Object obj = p;
}
}5、接口和抽象类
- 相同点
- 接口和抽象类都不能被实例化
- 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须事先这些抽象方法
- 不同点
- 接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法
- 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量
- 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作
- 接口里不能包含初始化块;但抽象类则完全可以包含初始化块
- 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足
6、面向接口编程
- 简单工厂模式
- 命令模式
七、内部类
1、非静态内部类
- 外部类2个作用域:public和省略访问控制符
- 内部类4个作用域
- 非静态内部类里不能有静态方法、静态成员变量、静态初始化块
public class Cow {
private double weight;
// 外部类的两个重载的构造器
public Cow() {
}
public Cow(double weight) {
this.weight = weight;
}
// 定义一个非静态内部类
private class CowLeg {
// 非晶态内部类的两个实例变量
private double length;
private String color;
// 非静态内部类的两个重载的构造器
public CowLeg() {
}
public CowLeg(double length, String color) {
this.length = length;
this.color = color;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
// 非静态内部类的实例方法
public void info() {
System.out.println("当前牛腿颜色是: " + color + ",高: " + length);
// 直接访问外部类的private修饰的成员变量
System.out.println("本牛腿所在奶牛重: " + weight);
}
}
public void test() {
CowLeg c1 = new CowLeg(1.12, "黑白相间");
c1.info();
}
public static void main(String[] args) {
Cow cow = new Cow(378.9);
cow.test();
}
}- 外部类成员变量、内部类成员变量与内部类里方法的局部变量同名,则可以通过使用this、外部类类名.this作为限定区分
public class DiscernVariable {
private String prop = "外部类的实例变量";
public class InClass {
private String prop = "内部类的实例变量";
public void info() {
String prop = "局部变量";
// 通过外部类类名.this.varName访问外部类实例变量
System.out.println("外部类的实例变量值: " + DiscernVariable.this.prop);
// 通过this.varName访问内部类实例的变量
System.out.println("内部类的实例变量值: " + this.prop);
// 直接访问局部变量
System.out.println("局部变量的值: " + prop);
}
}
public void test() {
InClass in = new InClass();
in.info();
}
public static void main(String[] args) {
new DiscernVariable().test();
}
}- 非静态内部类的成员可以访问外部类的private成员,但反过来就不成立了
public class Outer {
private int outProp = 9;
class Inner {
private String inProp = "内部类的实例变量";
public void accessOuterProp() {
// 非静态内部类可以直接访问外部类的private成员变量
System.out.println("外部类的outProp值: " + outProp);
}
}
public void accessInnerProp() {
// 外部类不能直接访问非静态内部类的实例变量
// 下面代码出现编译错误
// System.out.println("内部类的inProp值: " + inProp);
// 如需访问内部类的实例变量,必须显示创建内部类对象
System.out.println("内部类的inProp值: " + new Inner().inProp);
}
public static void main(String[] args) {
// 执行下面代码,值创建了外部类对象,还未创建内部类对象
Outer out = new Outer();
out.accessInnerProp();
}
}2、静态内部类
- 静态内部类可以包含静态成员,也可以包含非静态成员
public class AccessStaticInnerClass {
static class StaticInnerClass {
private static int prop1 = 5;
private int prop2 = 9;
}
public void accessInnerProp() {
// System.out.println(prop1);
// 上面代码出现错误,应改为如下形式
// 通过类名访问静态内部类的类成员
System.out.println(StaticInnerClass.prop1);
// System.out.println(prop2);
// 上面代码出现错误,应改为如下形式
// 通过实例访问静态内部类的实例成员
System.out.println(new StaticInnerClass().prop2);
}
}- 接口内部类只能是静态内部类,访问控制符只能是public,默认使用public static
3、使用内部类
- 在外部内内部使用内部类
- 在外部类以外使用非静态内部类:OuterClass.InnerClass varName 、 OuterInstance.new InnerConstructor();
- 如果希望在外部类以外的地方访问内部类(包括静态和非静态两种),则内部类不能使用private访问控制权限
class Out{
//定义一个内部类,不使用访问控制符
//即只有同一个包中的其他类可访问该内部类
class In{
public In(String msg) {
System.out.println(msg);
}
}
}
public class CreateInnerInstance {
public static void main(String[] args) {
// Out.In in = new Out().new In("测试信息");
Out.In in;
Out out = new Out();
in = out.new In("测试信息");
}
}- 在外部类以外使用静态内部类:new OuterClass.InnerConstructor()
class StaticOut {
// 定义一个静态内部类,不使用访问控制符
// 即只有同一个包中的其他类可访问该内部类
static class StaticIn {
public StaticIn() {
System.out.println("静态内部类的构造器");
}
}
}
public class CreateStaticInnerInstance {
public static void main(String[] args) {
// StaticOut.StaticIn in = new StaticOut.StaticIn();
StaticOut.StaticIn in;
in = new StaticOut.StaticIn();
}
}4、局部内部类
- 不能使用访问控制符和static修饰符修饰
public class LocalInnerClass {
public static void main(String[] args) {
// 定义局部内部类
class InnerBase {
int a;
}
// 定义局部内部类的子类
class InnerSub extends InnerBase {
int b;
}
// 创建局部内部类的对象
InnerSub is = new InnerSub();
is.a = 5;
is.b = 8;
System.out.println("InnerSub对象的a和b实例变量是: " + is.a + "," + is.b);
}
}5、Java 8 改进的匿名内部类
- 匿名内部类适合创建那种只需要一次使用的类,不能重复使用
- 匿名内部类不能是抽象类
- 匿名内部类不能定义构造器
new 实现接口() | 父类构造器(实参列表)
{
//匿名内部类的类体部分
}八、Java 8 新增的Lambda表达式
1、Lambda表达式入门
三部分组成
- 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略
- 箭头(->)。必须通过英文中画线和大于符号组成。
- 代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值。
interface Eatable {
void taste();
}
interface Flyable {
void fly(String weather);
}
interface Addable {
int add(int a, int b);
}
public class LambdaQs {
// 调用该方法余姚Eatable对象
public void eat(Eatable e) {
System.out.println(e);
e.taste();
}
// 调用该方法需要Flyable对象
public void drive(Flyable f) {
System.out.println("我正在驾驶: " + f);
f.fly("【碧空如洗的晴日】");
}
// 调用该方法需要Addable对象
public void test(Addable add) {
System.out.println("5与3的和为: " + add.add(5, 3));
}
public static void main(String[] args) {
LambdaQs lq = new LambdaQs();
// Lambda表达式的代码块只有一条语句,可以省略花括号
lq.eat(() -> System.out.println("苹果的味道不错"));
// Lambda表达式的形参列表只有一个形参,可以省略圆括号
lq.drive(weather -> {
System.out.println("今天天气是: " + weather);
System.out.println("直升机飞行平稳");
});
// Lambda表达式的代码块只有一句话,可以省略花括号
// 代码块中只有一条语句,即使表达式需要返回值,也可以省略return关键字
lq.test((a, b) -> a + b);
}
}2、Lambda表达式与函数式接口
- Lambda表达式的类型,也被称为“目标类型”,Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
九、枚举类
1、枚举类入门
- 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口
- 使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类
- 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰;如果强制指定访问控制符,则只能指定private修饰符
- 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加public static final修饰,无须程序员显示添加
枚举类默认提供了一个values()方法,该方法可以很方便地遍历所有的枚举值
public enum SeasonEnum {
//在第一行列出4个枚举实例
SPRING,SUMMER,FALL,WINTER;
}Test类
public class EnumTest {
public void judge(SeasonEnum s) {
// switch语句里的表达式可以是枚举值
switch (s) {
case SPRING:
System.out.println("春");
break;
case SUMMER:
System.out.println("夏");
break;
case FALL:
System.out.println("秋");
break;
case WINTER:
System.out.println("冬");
break;
}
}
public static void main(String[] args) {
// 枚举类默认有一个values()方法,返回该枚举类的所有实例
for (SeasonEnum s : SeasonEnum.values()) {
System.out.println(s);
}
// 使用枚举实例时,可通过EnumClass.variable形式来访问
new EnumTest().judge(SeasonEnum.SPRING);
}
}2、枚举类的成员变量、方法和构造器
public enum Gender {
//此处的枚举值必须调用对应的构造器来创建
MALE("男"),FEMALE("女");
private final String name;
//枚举类的构造器只能使用private修饰
private Gender(String name){
this.name = name;
}
public String getName() {
return this.name;
}
}Test类
public class GenderTest {
public static void main(String[] args) {
// 通过Enum的valueOf()方法来获取指定枚举类的枚举值
Gender g = Enum.valueOf(Gender.class, "FEMALE");
g.setName("女");
System.out.println(g + "代表: " + g.getName());
// 此时设置name值时将会提示参数错误
g.setName("男");
System.out.println(g + "代表: " + g.getName());
}
}十、对象在内存中的状态
- 可达状态:当一个对象被创建后,若有一个以上的引用变量引用它,则这个对象在程序中处于可达状态,程序可通过引用变量来调用该对象的实例变量和方法。
- 可恢复状态:系统的垃圾回收机制准备回收该对象所占用的内存,在回收该对象之前,系统会调用所有可恢复状态对象的finalize()方法进行资源清理。如果系统在调用finalize()方法时重新让一个引用变量引用该对象,则这个对象会再次变为可达状态;否则该对象将进入不可达状态。
- 不可达状态:当对象与所有引用变量的关联都被切断,且系统已经调用所有对象的finalize()方法后依然没有使该对象变成可达状态,那么这个对象将永久性地失去引用,最后变成不可达状态。只有当一个对象处于不可达状态是,系统才会真正回收该对象所占有的资源。
2、强制垃圾回收
- 调用System类的gc()静态方法:System.gc()。
- 调用Runtime对象的gc实例方法:Runtime.getRuntime.gc()。

阿里云工作强度 727人发布