设计模式
模板方法
模板方法(Template Method):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。明确一部分功能,而另一部分功能不明确。需要延伸到子类中实现
package alive.design;
/**
* @Author zyj
* @Date 2024/10/01 15:50
* @Description
*/
public abstract class Animal {
public abstract void call();
}
package alive.design;
/**
* @Author zyj
* @Date 2024/10/01 15:51
* @Description
*/
public class Cat extends Animal {
@Override
public void call() {
System.out.println("cat call");
}
}
package alive.design;
/**
* @Author zyj
* @Date 2024/10/01 15:51
* @Description
*/
public class Dog extends Animal {
@Override
public void call() {
System.out.println("dog call");
}
}
package alive.design;
/**
* @Author zyj
* @Date 2024/10/01 15:53
* @Description
*/
public class Test {
public static void main(String[] args) {
new Dog().call();
new Cat().call();
}
}
单例模式
目的:单例,让一个类只产生一个对象;
分类:
① 饿汉式
② 懒汉式
饿汉式
package alive.design;
/**
* @Author zyj
* @Date 2024/10/01 15:57
* @Description
*/
public class Person {
/*对象私有化*/
private Person() {
}
// 随着类的加载而加载
private static Person person = new Person();
// 将内部new 的对象给外界
public static Person getPerson() {
return person;
}
}
懒汉式
package alive.design;
/**
* @Author zyj
* @Date 2024/10/01 16:01
* @Description
*/
public class Util {
private Util() {
}
private static Util util = null;
public static Util getUtil() {
if (util == null) {
synchronized (Util.class) {
if (util == null) {
util = new Util();
}
}
}
return util;
}
}
package alive.design;
/**
* @Author zyj
* @Date 2024/10/01 15:53
* @Description
*/
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Util util = Util.getUtil();
System.out.println("util = " + util);
}
}
}
Lombok
简化JavaBean开发
- 下载插件
- 导入Jar包
Jar包链接
Lombok通过增加一些“处理程序”,可以让java变得简洁
Lombok能以注解形式简化JavaBean代码,提高开发效率。开发中经常需要写Javabean,都需要去增加对应的getter/setter,构造器、equals等方法,需要维护。
Lombok能通过注解的方式,在编译时自动为属性生成构造器,getter、setter、equals、hashCode、toString方法。
package alive.lombok;
import lombok.Data;
/**
* @Author zyj
* @Date 2024/10/01 16:15
* @Description
*/
//@Getter
//@Setter
//@ToString
//@AllArgsConstructor
//@NoArgsConstructor
@Data
public class Person {
private String name;
private Integer age;
}
@Data
不提供有参构造
JDK新特性
Lambda 表达式
格式:
() -> {}
():重写方法的参数位置
->:将参数传入方法体中
{}:重写方法的方法体
package alive.lambda;
/**
* @Author zyj
* @Date 2024/10/01 16:57
* @Description
*/
public class T1 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("执行了");
}
}).start();
// lambda表达式
new Thread(() -> System.out.println("执行了"));
}
}
使用
① 必须是函数式接口作方法参数传递
② 有且只有一个抽象方法的接口,用@FunctionalInterface
去检测
package alive.lambda;
/**
* @Author zyj
* @Date 2024/10/01 17:40
* @Description
*/
@FunctionalInterface
public interface USB {
public void open();
}
① 观察是否是函数式接口作方法参数传递
② 调用方法,以匿名内部类的形式传递实参
③ 改造
package alive.lambda;
/**
* @Author zyj
* @Date 2024/10/01 17:40
* @Description
*/
@FunctionalInterface
public interface USB {
public void open();
}
package alive.lambda;
/**
* @Author zyj
* @Date 2024/10/01 17:43
* @Description
*/
public class T2 {
public static void main(String[] args) {
open(new USB() {
@Override
public void open() {
System.out.println("open");
}
});
open(() -> {
System.out.println("open");
});
}
public static void open(USB usb) {
usb.open();
}
}
省略规则
① 重写方法的参数类型可省略
② 重写方法的参数只有一个可省略()
③ 方法体中,代码只有一行可省略{}
④ 方法体中,代码只有一行且return
,可省略{} ;
package alive.lambda;
import java.util.ArrayList;
import java.util.Collections;
/**
* @Author zyj
* @Date 2024/10/01 17:52
* @Description
*/
public class T3 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("Tom", 20));
list.add(new Person("Alex", 10));
list.add(new Person("Jack", 21));
// Collections.sort(list, new Comparator<Person>() {
// @Override
// public int compare(Person o1, Person o2) {
// return o1.getAge() - o2.getAge();
// }
// });
System.out.println("list = " + list);
Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());
System.out.println("list = " + list);
}
}
函数式接口
Supplier
Supplier接口
java.util.function.Supplier< T >接口,它意味着“供给”
方法:
T get() 我们需要什么,get方法就可以返回什么
需求:
使用Supplier接口作为方法的参数
用Lambda表达式求出int数组中的最大值
泛型:
<引用数据类型> 规定了需要操作的数据类型
package alive.inter;
import java.util.Arrays;
import java.util.function.Supplier;
/**
* @Author zyj
* @Date 2024/10/01 20:05
* @Description
*/
public class T1 {
public static void main(String[] args) {
sum(new Supplier<Integer>() {
@Override
public Integer get() {
int[] arr = {1, 2, 3, 4, 5, 6};
Arrays.sort(arr);
return arr[arr.length - 1];
}
});
sum(() -> {
int[] arr = {6, 1, 2, 1, 4};
Arrays.sort(arr);
return arr[arr.length - 1];
});
}
public static void sum(Supplier<Integer> supplier) {
Integer max = supplier.get();
System.out.println("max = " + max);
}
}
Consumer
java.util.function.consumer< T > 消费型接口
方法:
void accept(T t),意味着消费一个指定的泛型数据
package alive.inter;
import java.util.function.Consumer;
/**
* @Author zyj
* @Date 2024/10/01 20:15
* @Description
*/
public class T2 {
public static void main(String[] args) {
print(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("s = " + s);
}
}, "alex");
print(s -> {
System.out.println("s = " + s);
}, "Tom");
}
public static void print(Consumer<String> consumer, String s) {
consumer.accept(s);
}
}
Function
java.util.function.Function< T,R >:接口用来根据一个类型得到另一个类型的数据
方法:
R apply(T t):根据类型T参数获取类型R的结果
package alive.inter;
import java.util.function.Function;
/**
* @Author zyj
* @Date 2024/10/01 20:28
* @Description
*/
public class T3 {
public static void main(String[] args) {
func(new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return integer + "";
}
}, 100);
func(i -> {
return i + "";
}, 10);
}
public static void func(Function<Integer, String> function, Integer number) {
String str = function.apply(number);
System.out.println("str = " + str + "a");
}
}
Predicate
java.util.function.Predicate< T >接口,判断型接口
boolean test< T t > 用于判断的方法,返回值为boolean型
package alive.inter;
import java.util.function.Predicate;
/**
* @Author zyj
* @Date 2024/10/01 20:37
* @Description
*/
public class T4 {
public static void main(String[] args) {
isBoolean(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() == 7;
}
}, "1234567");
isBoolean(s -> s.length() == 7, "123457");
}
public static void isBoolean(Predicate<String> predicate, String s) {
boolean test = predicate.test(s);
System.out.println("test = " + test);
}
}
Stream流
Stream流中的“流”不是特指“IO流”,它是一种“流式编程”(编程方式),可以看做是“流水线”
package alive.inter;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/01 21:23
* @Description
*/
public class T5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张AA");
list.add("张B");
list.add("张CC");
list.add("李D");
list.add("王EE");
list.add("张FF");
Stream<String> stream = list.stream();
stream.filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
}
}
Stream的获取
① 针对集合,Collection中的方法:
Stream< E > stream()
② 针对数组,Stream接口的静态方法:
static < T > Stream < T > of(T ...value)
package alive.inter;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/01 21:39
* @Description
*/
public class T6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
Stream<String> stream1 = list.stream();
System.out.println("stream1 = " + stream1);
Stream<String> stream2 = Stream.of("张三", "李四", "王五");
System.out.println("stream2 = " + stream2);
//stream1 = java.util.stream.ReferencePipeline$Head@214c265e
//stream2 = java.util.stream.ReferencePipeline$Head@448139f0
}
}
Stream的方法
forEach
Stream 中的forEach方法:void forEach(Consumer< ? super T> action)
forEach:逐一遍历,是一个终结方法,使用完之后Stream流就不能再使用
package alive.inter;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:16
* @Description
*/
public class T7 {
public static void main(String[] args) {
print();
}
public static void print() {
Stream<String> stream = Stream.of("A", "B", "C", "D");
stream.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("s = " + s);
}
});
stream.forEach(s -> System.out.println(s));
}
}
第二个forEach就会报错
Count()
long count()
统计元素个数
count也是个终结方法
package alive.inter;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:28
* @Description
*/
public class T8 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("A", "B", "C", "D");
long count = stream.count();
System.out.println("count = " + count);
}
}
filter()
Stream< T > filter(Predicate< ? super T> predicate)
方法,返回一个新的Stream流对象
根据某个条件进行元素过滤
package alive.inter;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:33
* @Description
*/
public class T9 {
public static void main(String[] args) {
Stream<Person> stream = Stream.of(new Person(10, "Tom"), new Person(20, "Jack"), new Person(30, "Alex"));
stream.filter(p -> p.getAge() <= 10).forEach(p -> {
System.out.println("p = " + p);
});
}
}
limit()
Stream < T > limit(long maxSize)
:获取Stream流对象中的前n个元素,返回一个新的Stream流对象
package alive.inter;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:39
* @Description
*/
public class T10 {
public static void main(String[] args) {
Stream<Person> stream = Stream.of(new Person(10, "Tom"), new Person(20, "Jack"), new Person(30, "Alex"));
stream.limit(1).forEach(p -> {
System.out.println("p = " + p);
});
}
}
skip
Stream < T > skip(long n)
:跳过Stream流对象中的前n个元素,返回一个新的Stream流对象
package alive.inter;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:44
* @Description
*/
public class T11 {
public static void main(String[] args) {
Stream<Person> stream = Stream.of(new Person(10, "Tom"), new Person(20, "Jack"), new Person(30, "Alex"));
stream.skip(2).forEach(p -> {
System.out.println("p = " + p);
});
//p = Person(age=30, name=Alex)
}
}
concat()
static < T > Stream< T >concat(Stream < ? extends T> a,Stream < ? extends T> b)
:将两个Stream流合并成一个
package alive.inter;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:48
* @Description
*/
public class T12 {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("A", "B", "C");
Stream<String> stream2 = Stream.of("D", "E", "F");
Stream.concat(stream2, stream1).forEach(s -> {
System.out.println("s = " + s);
});
//s = D
//s = E
//s = F
//s = A
//s = B
//s = C
}
}
collect()
从Stream流对象转成集合对象
package alive.inter;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:52
* @Description
*/
public class T13 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("A", "B", "C");
List<String> list = stream.collect(Collectors.toList());
}
}
dinstinct()
Stream< T > distinct()
元素去重复,依赖hashCode和equals方法
package alive.inter;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:55
* @Description
*/
public class T14 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("A", "B", "C", "C");
stream.distinct().forEach(s -> {
System.out.println("s = " + s);
});
//s = A
//s = B
//s = C
}
}
package alive.inter;
import lombok.Data;
/**
* @Author zyj
* @Date 2024/10/02 10:33
* @Description
*/
@Data
public class Person {
public Person() {
}
public Person(Integer age, String name) {
this.age = age;
this.name = name;
}
private Integer age;
private String name;
}
package alive.inter;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 10:55
* @Description
*/
public class T14 {
public static void main(String[] args) {
Stream<Person> stream = Stream.of(new Person(10, "Tom"), new Person(10, "Tom"), new Person(30, "Alex"));
stream.distinct().forEach(person -> {
System.out.println("person = " + person);
});
//person = Person(age=10, name=Tom)
//person = Person(age=30, name=Alex)
}
}
map
``Stream< R > map(Function<T , R> mapper):转换流中的数据类型
package alive.inter;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 11:03
* @Description
*/
public class T15 {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7);
stream.map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return integer + "";
}
}).forEach(i -> {
System.out.println("i = " + i);
});
}
}
方法引用
引用方法
使用:
① 被引用的方法要写在重写方法中
② 被引用的方法从参数上,返回值上要和所在重写方法一致,而且引用的方法最好是操作重写方法的参数值的
③ 去除重写方法的参数
,去除->
,去除引用方法的参数,将被引用的方法.
改成::
package alive.method;
import java.util.stream.Stream;
/**
* @Author zyj
* @Date 2024/10/02 12:09
* @Description
*/
public class T1 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("A", "B", "C");
// stream.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// stream.forEach(s -> System.out.println(s));
stream.forEach(System.out::println);
}
}
对象名-引用成员方法
使用对象名引用成员方法
格式:
对象::
成员方法名
函数式接口:supplier
java.util.function.Supplier< T >接口
抽象方法:
T get(),用来获取一个泛型参数指定类型的对象数据
Supplier接口使用什么泛型,就可以使用get获取一个什么类型的数据
package alive.method;
import java.util.function.Supplier;
/**
* @Author zyj
* @Date 2024/10/02 12:29
* @Description
*/
public class T2 {
public static void main(String[] args) {
print(new Supplier<String>() {
@Override
public String get() {
return " abc ".trim();
}
});
print(() -> " abc ".trim());
print(" abc "::trim);
}
public static void print(Supplier<String> supplier) {
String str = supplier.get();
System.out.println("str = " + str);
}
}
类名-引用静态方法
格式
类名::
静态成员方法
package alive.method;
import java.util.function.Supplier;
/**
* @Author zyj
* @Date 2024/10/02 12:40
* @Description
*/
public class T3 {
public static void main(String[] args) {
print(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
print(() -> Math.random());
print(Math::random);
}
public static void print(Supplier<Double> supplier) {
Double aDouble = supplier.get();
System.out.println("aDouble = " + aDouble);
}
}
类-构造引用
类 - 构造方法引用
格式:
构造方法名::
new
函数式接口:Function
java.util.function.Function< T,R>接口
抽象方法
R apply(T t),根据类型T的参数获取类型R的结果。用于数据类型转换
package alive.method;
import java.util.function.Function;
/**
* @Author zyj
* @Date 2024/10/02 12:43
* @Description
*/
public class T4 {
public static void main(String[] args) {
print(new Function<String, Person>() {
@Override
public Person apply(String s) {
return new Person(s);
}
}, "Tom");
print(s -> new Person(s), "Jack");
print(Person::new, "Alex");
}
public static void print(Function<String, Person> function, String name) {
Person p = function.apply(name);
System.out.println("p = " + p);
}
}
数组-数组引用
格式:
数组的数据类型[]::new
int [] :: new 创建一个int型的数组
double[] ::new 创建一个double型的数组
package alive.method;
import java.util.function.Function;
/**
* @Author zyj
* @Date 2024/10/02 13:23
* @Description
*/
public class T5 {
public static void main(String[] args) {
print(new Function<Integer, int[]>() {
@Override
public int[] apply(Integer integer) {
return new int[integer];
}
}, 10);
print(length -> new int[length], 2);
print(int[]::new, 4);
}
public static void print(Function<Integer, int[]> function, Integer length) {
int[] arr = function.apply(length);
System.out.println("arr = " + arr.length);
}
}
JDK8之后的新特性
接口的私有方法
Java8版本接口直接了两类成员:
- 公共的默认方法
- 公共的静态方法
Java9版本接口又新增了一类成员:
- 私有方法:因为有了默认方法和静态方法这样具有具体实现的方法,那么就可能出现多个方法由共同的代码可以抽取,而这些共同的代码抽取出来的方法又只希望在接口内部使用,所以就增加了私有方法。
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 13:30
* @Description
*/
public interface USB {
private void open() {
System.out.println("私有非静态方法");
}
private static void close() {
System.out.println("私有静态方法");
}
public default void def() {
open();
close();
}
}
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 13:33
* @Description
*/
public class USBImpl implements USB {
}
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 13:33
* @Description
*/
public class T1 {
public static void main(String[] args) {
USBImpl usb = new USBImpl();
usb.def();
}
}
砖石操作符与匿名内部类结合
自Java9 之后我们将能够与匿名实现类共同使用砖石操作符,即匿名实现类也支持类型自动推断
package alive.newJDK;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* @Author zyj
* @Date 2024/10/02 13:49
* @Description
*/
public class T2 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person(10, "Tom"));
list.add(new Person(9, "Jack"));
list.add(new Person(8, "Alex"));
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());
System.out.println("list = " + list);
}
}
Java 8 的语言等级编译会报错,cannot be used with anonymous classes
Java 9 以上版本才能编译和运行
try…catch升级
try(IO流对象1声明和初始化;IO流对象2声明和初始化){
可能出现异常的代码
}catch(异常类型 对象名){
异常处理方案
}
JDK1.9对trywitch-resources的语法升级了
- 该资源必须实现java.io.closeable接口
- 在try子句中声明并初始化资源对象,也可以直接使用已初始化的资源对象
- 该资源对象必须是final的
IO流对象1 声明和初始化
IO流对象2 声明和初始化
try(IO流对象1,IO流对象2){
可能出现异常的代码
} catch(异常类型 对象名){
异常处理方案
}
局部变量类型自动推断
JDK 10 之前,我们定义局部变量都必须要明确数据的数据类型,但是到了JDK 10,出现了局部变量类型推断,顾名思义,就是定义局部变量时,不用先确定具体的数据类型,可以根据具体数据推断出所属的数据类型。
var 变量名 = 值
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 14:05
* @Description
*/
public class T3 {
public static void main(String[] args) {
var i = 10;
System.out.println(i);
var arr = new int[]{1, 2, 3, 4, 5};
for (int el : arr) {
System.out.println("el = " + el);
}
}
}
switch
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 14:50
* @Description
*/
public class T4 {
public static void main(String[] args) {
int sco = 3;
switch (sco) {
case 1, 2:
System.out.println("差");
break;
case 3, 4:
System.out.println("中");
break;
case 5, 6:
System.out.println("优");
break;
default:
System.out.println("未知");
break;
}
// JDK 12
switch (sco) {
case 1, 2 -> System.out.println("差");
case 3, 4 -> System.out.println("中");
case 5, 6 -> System.out.println("优");
default -> System.out.println("未知");
}
// JDK 13
var res = switch (sco) {
case 1, 2 -> {
yield "差";
}
case 3, 4 -> {
yield "中";
}
case 5, 6 -> {
yield "优";
}
default -> {
yield "未知";
}
};
System.out.println("res = " + res);
}
}
文本块
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 15:12
* @Description
*/
public class T5 {
public static void main(String[] args) {
var str = """
<html>
<body>
<p> hello world </p>
</body>
</html>
""";
System.out.println("str = " + str);
}
}
instanceof 模式匹配
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 15:46
* @Description
*/
public class T6 {
public static void main(String[] args) {
action(new Dog());
action(new Cat());
}
public static void action(Animal animal) {
if (animal instanceof Dog dog) {
dog.pro();
dog.call();
} else if (animal instanceof Cat cat) {
cat.call();
}
}
}
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 15:44
* @Description
*/
public abstract class Animal {
public abstract void call();
}
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 15:45
* @Description
*/
public class Cat extends Animal {
@Override
public void call() {
System.out.println("cat call");
}
}
package alive.newJDK;
/**
* @Author zyj
* @Date 2024/10/02 15:45
* @Description
*/
public class Dog extends Animal {
@Override
public void call() {
System.out.println("dog call");
}
public void pro() {
System.out.println("保护主人");
}
}
Record类
package alive.a_newJDK;
public record Person(String name) {
/**
* ① 不能声明实例变量
* ② 可以声明静态变量
* ③ 不能声明空参构造
* ④ 可以声明静态方法
* ⑤ 可以声明非静态方法
*/
// int i;
// public Person(){}
public static void print() {
}
public void call() {
}
}
package alive.a_newJDK;
/**
* @Author zyj
* @Date 2024/10/02 15:55
* @Description
*/
public class T1 {
public static void main(String[] args) {
Person tom = new Person("Tom");
System.out.println("tom = " + tom);
}
}
密封类
很多语言都有密封类的概念,在Java语言中,最早就有密封类的思想,就是final修饰的类,该类不允许被继承。而JDK15开始,针对密封类进行了升级
Java15 通过密封的类和接口来增强Java编程而言,这是新引入的预览功能并在Java 16中进行了二次预览,并在Java 17最终确定下来。这个预览功能用于限制超类的使用,密封的类和接口限制其他可能继承或实现他们的其他类或接口
【修饰符】
sealed class
密封类 【extend
父类】【implements
父接口】permits
子类{}
【修饰符】sealed interface
接口 【extends
父接口们】permits
实现类{}
- 密封类用
sealed
修饰符来修饰 - 使用
permits
关键字来指定可以继承或实现该类的类型有哪些 - 一个类继承密封类或实现密封接口,该类必须是
sealed 、non-sealed、final
修饰的 - sealed修饰的类或接口必须有子类或实现类
public sealed class Animal permits Dog,Cat{
}
public non-sealed class Dog extends Animal{
}
public non-sealed class Cat extends Animal{
}