狂Java讲义(读书笔记)(第五章)
作者:互联网
第五章面向对象(下)
5.1 Java 8 增强的包装类
把字符串类型的值转换为基本类型的值有两种方式。
- 利用包装类提供的parseXxx(String s)静态(除了Character之外的所有包装类都提供了该方法)。
- 利用包装类提供的Xxx(String s)构造器
String类提供了多个重载valueOf()方法,用于将基本类型变量转换成字符串,下面程序示范了这种各类型转换关系。
public class Student{
public static void main(String[] args) {
String intStr = "123";
//把一个特定字符串转换成int变量
int it1 = Integer.parseInt(intStr);
int it2 = new Integer(intStr);
System.out.println(it2);
String floatStr = "4.56";
//把一个特定字符串转换成float变量
float ft1 = Float.parseFloat(floatStr);
float ft2 = new Float(floatStr);
System.out.println(ft2);
//把一个double变量转换成String变量
String dbStr = String.valueOf(3.344);
System.out.println(dbStr);
//把一个boolean变量转换成String变量
String boolStr = String.valueOf(true);
System.out.println(boolStr);
}
}
Java8为Integer、Long增加了如下方法。
- static String toUnsignedString(int/long i):该方法将指定int或long行整数转换为无符号整数对应的字符串。
- static String toUnsignedString(int i/long, int radix):该方法将指定int或long型整数转换为指定进制的无符号整数对应的字符串。
- static xxx pareUnsignedXxx(String s):该方法将指定字符串解析成无符号整数。当调用类为Integer时,xxx代表int;当调用类是Long时,xxx代表long。
- static xxx parseUnsignedXxx(String s, int redix):该方法将指定字符串按指定进制解析成无符号整数。当调用类为Integer时,xxx代表int;当调用类时Long时,xxx代表long。
- static int compareUnsigned(xxx x, xxx y):该方法将x、y两个整数转换为无符号整数后比较大小。当调用类为Integer时,xxx代表int;当调用类是Long时,xxx代表long。
- static long divideUnsigned(long dividend, long divisor):该方法将x、y两个整数转换为无符号整数后计算它们相除的商。当调用类为Integer时,xxx代表int;当调用类时Long时,xxx代表long。
5.2 处理对象
5.2.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;
}
//省略color、weight的setter和getter方法
...
//重写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);
}
5.2.2 ==和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(z)多少次,返回的结果应该保持一致,要么一直是true,要么一直是false。
- 对任何不是null的x,x.equals(null)一定返回false。
Object默认提供的equals()只是比较对象的地址,即Object类的equals()方法比较的结果与==运算符比较的结果完全相同。因此在实际应用中常常需要重写equals()方法,重写equals方法时,相等条件时由业务要求决定的,因此equals()方法的实现也是由业务要求决定的。
5.3 类成员
static关键字修饰的成员就是类成员。
5.3.1 理解类成员
5.3.2 单例(Singleton)类
如果一个类始终只能创建一个实例,则这个类被称为单例类。
根据良好封装的原则:一旦把该类的构造器隐藏起来,就需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)。
除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象,也就无法保证之创建一个对象。为此该类需要使用一个成员变量来保存曾经创建的对象,因为该成员变量需要被上面的静态方法访问,故成员变量必须使用static修饰。
基于上面的介绍,下面程序创建了一个单例类。
class Singleton{
//使用一个类变量来缓存曾经创建的实例
private static Singleton instance;
//对构造器使用private修饰,隐藏该构造器
private Singleton(){}
//提供一个静态方法,用于返回Singletion实例
//该方法可以加入自定义控制,保证只产生一个Singleton对象
public static Singleton getInstance(){
//如果instance为null,则表明还不曾创建Singleton对象
//如果instance不为null,则表明已经创建了Singletion对象
//将不会重新创建新的实例
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
public class Student {
public static void main(String[] args) {
//创建Singleton对象不能通过构造器
//只能通过geInstance方法来得到实例
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);//将输出true
}
}
5.4 final修饰符
final关键字可用于修饰类、成员变量和方法,final关键字有点类似C#里面的sealed关键字,用于表示它修饰的类、方法和变量不可变。
5.4.1 final成员变量
对于final修饰的成员变量而言,一旦有了初始值,就不能被重新赋值,如果既没有在定义成员变量时指定初始值,也没有在初始化块、构造器中为成员变量指定初始值,那么这些成员变量的值将一直时系统默认分配的0、’\u0000’、false或null,这些成员变量也就完全失去了存在的意义。因此Java语法规定:final修饰的成员变量必须由程序员显式地指定初始值。
归纳起来,final修饰的类变量、实例变量能指定初始值的地方如下:
- 类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。
- 实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。
5.4.2 final局部变量
系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。因此使用final修饰局部变量时,既可以在定义时指定默认值,也可以不指定默认值。
如果final修饰的局部变量在定义时没有指定默认值,则可以在后面代码中对该final变量赋初始值,但只能一次,不能重复赋值:如果final修饰的局部变量在定义时已经制定默认值,则后面代码中不能再对该变量赋值。
public class Student {
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;
}
}
5.4.3 final修饰基本类型变量和引用类型变量的区别
import java.util.Arrays;
class Person{
private int age;
public Person(){}
public Person(int age){
this.age = age;
}
//省略age的setter和getter方法
...
}
public class Student {
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;
}
}
5.4.4 可执行“宏替换”的final变量
对于一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要该变量满足三个条件,这个final变量就不再是一个变量,而是相当于一个直接量。
- 使用final修饰符修饰。
- 在定义该final变量时制定了初始值。
- 该初始值可以在编译时就被确定下来。
5.4.5 final方法
final修饰的方法不可被重写,如果出于某些原因,不希望子类重写父类的某个方法,则可以使用final修饰该方法。
5.4.6 final 类
final修饰的类不可以有子类。
5.5 抽象类
5.5.1 抽象方法和抽象类
抽象方法和抽象类必须使用abstract修饰符定义,有抽象方法的类只能被定义成抽象类,抽象类里面可以没有抽象方法。
抽象方法和抽象类的规则如下:
- 抽象类必须使用abstract修饰符来修饰,抽象方法也必须由abstract修饰符来修饰,抽象方法不能有方法体。
- 抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里面不包含抽象方法,这个抽象类也不能创建实例。
- 抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。
- 含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。
5.5.2 抽象类的作用
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。
5.6 Java 8 改进的接口
5.6.1 接口的概念
5.6.2 Java 8 中接口的定义
和类定义不同,定义接口不再使用class关键字,而是使用interface关键字。定义接口的基本语法格式如下:
[修饰符] interface 接口名 extends 父接口1,父接口2...
{
零个到多个常量定义...
零个到多个抽象方法定义...
零个到多个内部类、接口、枚举定义...
零个到多个默认方法或类方法定义...
}
对上面语句的详细说明如下。
- 修饰符可以时public或者省略,如果省略了public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下才可以访问该接口。
- 接口名应与类名采用相同的命名规则,即如果仅从语法角度看,即接口名只要是合法的标识符即可;如果要遵循Java可读性规范,则接口名应由多个有意义的单词连缀而成。每个单词首字母大写,单词与单词之间无需任何分隔符。接口名通常能够使用形容词。
- 一个接口可以有多个直接父类,但接口只能继承接口,不能继承类。
下面定义一个接口
package lee;
public interface Output{
//接口里定义的成员变量只能是常量
int MAX_CACHE_LINE = 50;
//接口里定义的普通方法只能是public的抽象方法
void out();
void getData(String... msgs);
//在接口中定义默认方法,需要使用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 "接口里的方法";
}
}
5.6.3 接口的继承
接口的继承和类的继承不一样,接口完全支持多继承,即一个接口可以有多个直接父类。和类继承相似,子接口扩展某个父接口,将会获得里定义的所有抽象方法、常量。
5.6.4 使用接口
归纳起来,接口主要有如下用途。
- 定义变量,也可用于进行强制类型转换。
- 调用接口中定义的常量。
- 被其它类实现。
类实现接口的语法格式如下。
[修饰符] class 类名 extends 父类 implements 接口1,接口2...
{
类体部分
}
5.6.5 接口和抽象类
接口和抽象类很像,它们都具有如下特征。
- 接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其它类实现和继承。
- 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
接口和抽象类在用法上也存在如下差别。 - 接口里只能包含抽象方法、静态方法和默认方法不能为普通方法提供方法实现了;抽象类则完全可以包含普通方法。
- 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
- 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
- 接口里不能包含初始化块;但抽象类则完全可以包含初始化块。
- 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。
5.7 内部类
内部类住哟啊有如下作用。
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
- 内部类成员可以直接访问外部类的私有属性,因为内部类被当成其他外部类成员,同一个类的成员之间可以相互访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
- 匿名内部类适合用于创建那些仅需要一次使用的类。
从语法角度来看,定义内部类与定义外部类的语法大致相同,内部类除了需要定义在其他类里面之外,还存在如下两点区别。
- 内部类比外部类可以多使用三个修饰符:private、protected、static——外部类不可以使用这三个修饰符。
- 非静态内部类不能拥有静态成员。
5.7.1 非静态内部类
内部类定义语法格式如下。
public class OuterClass{
//此处可以定义内部类
}
非静态内部类里不能有静态方法、静态成员变量、静态初始化块。
5.7.2 静态内部类
如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类,有的地方也成为静态内部类。
public class StaticInnerClassTest{
private int prop1 = 5;
private static int pro2 = 9;
static class StaticInnerClass{
//静态内部类里可以包含静态成员
private static int age;
public void accessOuterProp(){
//下面代码出现错误
//静态内部类无法访问外部类的实例变量
System.out.println(pro1);
//下面代码正常
System.out.println(pro2);
}
}
}
外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。下面程序示范了这条规则。
public class AccessStaticInnerClass{
static class StaticInnerClass{
private static int prop1 = 5;
private int prop2 = 9;
}
public void accessInnerProp(){
//System.out.println(pro1);
//上面代码出现错误,应改为如下形式
//通过类名访问静态内部类的类成员
System.out.println(StaticInnerClass.prop1);
// System.out.println(prop2);
// 上面代码出现错误,应该为如下形式
// 通过实例访问静态内部类的实例变量
System.out.println(new StaticInnerClass().prop2);
}
}
5.7.3 使用内部类
- 在外部类内部使用内部类
- 在外部类以外使用非静态内部类
- 省略访问控制符的内部类,只能被与外部类处于同一个包中的其他类所访问。
- 使用protected修饰的内部类,可被与外部类处于同一个包中的其他类和外部类的子类所访问。
- 使用public修饰的内部类,可以在任何地方被访问。
在外部类以外的地方定义内部类(包括静态和非静态两种)变量的语法格式如下:
OuterClass.InnerClass varName
在外部类以外的地方创建非静态内部类实例的语法如下:
OuterInstance.new InnerConstructor()
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("测试信息");
/*
上面代码可改为如下三行代码
使用OutterClass.InnerClass的形式定义内部类变量
Out.In in
创建外部类实例,非静态内部类实例将寄生在该实例中
Out out = new Out();
通过外部实例和new来调用内部类构造器创建非静态内部类实例
in = out.new In("测试信息");
*/
}
}
上面程序粗体代码行创建了一个非静态内部类的对象。从上面代码可以看出,非静态内部类的构造器必须使用外部类对象来调用。
3.在外部类以外使用静态内部类
在外部类以外的地方创建静态内部类实例的语法如下:
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();
/*
上面代码可改为如下两行代码
使用OuterClass.InnerClass的形式定义内部类变量
StaticOut.StaticIn in;
通过new来调用内部类构造器创建静态内部类实例
in = new StaticOut.StaticIn();
*/
}
}
5.7.4 局部内部类
如果把一个内部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。
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.7.5 Java 8 改进的匿名内部类
定义匿名内部类的格式如下:
new 实现接口() | 父类构造器(实参列表){
//匿名内部类的类体部分
}
关于匿名内部类还有如下两条规则:
- 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许将匿名内部类定义成抽象类。
- 匿名内部类不能定义构造器。
最常用的创建匿名内部类的方式时许哟啊创建某个接口类型的对象,图下程序所示:
interface Product{
public double getPrice();
public String getName();
}
public class AnonymousTest {
public void test(Product p){
System.out.println("购买了一个" + p.getName() + ",花掉了" + p.getPrice());
}
public static void main(String[] args) {
AnonymousTest ta = new AnonymousTest();
//调用test()方法时,需要传入一个Product参数
// 此处传入其匿名实现类的实例
ta.test(new Product() {
@Override
public double getPrice() {
return 567.8;
}
@Override
public String getName() {
return "AGP显卡";
}
});
}
}
5.8 Java 8 新增的Lambda表达式
Lamdba表达式的主要作用就是代替匿名内部类的繁琐语句。它由三部分组成。
- 形参列表。
- 箭头(->)。
- 代码块。
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));
}
}
5.8.2 Lambda表达式与函数式接口
Lambda表达式实现的是匿名方法——因此他只能实现特定函数式接口中的唯一方法。这意味着Lambda表达式有如下两个限制:
- Lambda表达式的目标类型必须是明确的函数式接口。
- Lambda表达式之只能为函数式接口创建对象。
为保证Lambda表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式: - 将Lambda表达式赋值给函数式接口类型的变量。
- 将Lambda表达式作为函数是接口类型的参数传给某个方法。
- 使用函数式接口对Lambda表达式进行强制类型转换。
Java 8 在java.util.function包下预定义了大量函数式接口,典型地包含如下4类接口: - XxxFunction:
- XxxConsumer:
- XxxxPredicate:
- XxxSupplier:
5.8.3 方法引用和构造器引用
- 引用类方法
- 引用特定对象的实例方法
- 引用某类对象的实例方法
- 引用构造器
5.8.4 Lambda表达式与匿名内部类的联系和区别
Lambda表达式是匿名内部类的一种简化,因此它可以部分提取匿名内部类的作用,Lambda表达式与匿名内部类存在如下相同点:
- Lambda表达式与匿名内部了一样,都可以直接访问“effectively final”的局部变量,以及外部类的成员变量(包括实例变量和类变量)。
- Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。
Lambda表达式与匿名内部类主要存在如下区别: - 匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但Lambda表达式只能为函数式接口创建实例。
- 匿名内部类可以为抽象类甚至普通类创建实例;但Lambda表达式只能为函数式接口创建实例。
- 匿名内部类是实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口的默认方法。
5.8.5 使用Lambda表达式调用Arrays的类方法
5.9 枚举类
在某些情况下,一个类的对象是有限而且固定的,比如季节类,它只有4个对象;再比如行星类,目前只有8个对象。这种实例有限而且固定的类,在Java里被称为枚举类。
5.9.1 手动实现枚举类
在早期代码中,可能会直接使用简单的静态常量来表示枚举,例如:
public static final int SEASON_SPRING = 1;
public static final int SEASON_SUMMER = 2;
public static final int SEASON_FALL = 3;
public static final int SEASON_WINTER = 4;
这种方法简单明了,但存在如下几个问题:
- 类型不安全
- 没有命名空间
- 打印输出的意义不大
但枚举又确实有存在的意义,因此早期也可采用通过定义类的方式来实现,可以采用如下设计方式:
- 通过private将构造器隐藏起来
- 把这个类的所有可能实例都使用public static final修饰的类变量来保存
- 如果有必要,可以提供一些静态方法,允许其他程序根据特定参数来获得与之匹配的实例
- 使用枚举类可以使程序更加健壮,避免创建对象的随意性
5.9.2 枚举类入门
Java 5 新增了一个enum关键字(它与class、interface关键字的地位相同),用于定义枚举类。
但枚举类终究不是普通类,它与普通类有如下简单区别:
- 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承其他父类。
- 使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
- 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰;如果强调指定访问控制符,则只能指定private修饰符。
- 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。
java.lang.Enum类中提供了如下几个方法:
- int compareTo(E o):
- String name():
- int ordinal():
- String toString():
- public static <T extends Enum>T
valueOf(Class<T>enumType,String name);
5.9.3 枚举类的成员变量、方法和构造器
枚举类也是一种类,只是它是一种比较特殊的类,因此它一样可以定义成员变量,方法和构造器。
5.9.4 实现接口的枚举类
5.9.5 包含抽象方法的枚举类
5.10 修饰符的使用范围
本章练习
1.通过抽象类定义车类的模板,然后通过抽象的车类来派生拖拉机、卡车、小轿车。
2.定义一个接口,并使用匿名内部类方式创建接口的实例。
3.定义一个函数式接口,并使用Lambda表达式创建函数式接口的实例。
4.定义一个类,该类用于封装一桌梭哈游戏,这个类应该包含桌上剩下的牌的信息,并包含5个玩家的状态信息:他们各自的位置、游戏状态(正在游戏或已放弃)、手上已有的牌等信息。如果有可能,这个类还应该实现发牌方法,这个方法需要控制从谁开始发牌,不要发牌给放弃的人,并修改桌上剩下的牌。
标签:Java,内部,读书笔记,final,接口,讲义,String,public,变量 来源: https://blog.csdn.net/iamaowu/article/details/119299863