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()。