- 设计模式(重点)
概述:是一套经人反复使用的代码设计经验的总结
也可以理解为特定问题的特定的解决方案
好处:可读性更强,更容易复用
设计模式的归类,分三大类:(23种)
1.对象型模式:对象的创建方式;例如:单例,工厂模式等
2.结构型模式:组织结构的包装;例如:装饰者模式(处理流),代理模式等
3.行为型模式:对象的行为监听;例如:观察者模式等
1.1 工厂模式(重点)
/*
概述:从工厂中根据需求,获取产品(对象)
两种工厂模式:1.静态工厂; 2.实例工厂
静态工厂使用:工厂类.静态方法 new 工厂对象.成员方法
案例:从肉工厂中获取产品;例如1号车间-牛肉;2号车间-猪肉
*
* */
class Factory{
public static Object getObject(int num) {
//违背了OCP原则:对外新增代码持开放状态,对内修改的代码持关闭状态
//此处应该要固定好,增强可维护性
if(num==1) {
return new Beef();
}else if(num==2) {
return new Pig();
}
return null;
}
public static Object getObject(Class clazz) throws InstantiationException, IllegalAccessException {
return clazz.newInstance();
}
}
class Beef{
}
class Pig{
}
public class Test1 {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
System.out.println("请输入你要获取的产品:1.牛肉,2.猪肉");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
//1.基本的静态工厂
//弊端:违背ocp原则的设计,不够灵活,维护性差
/*Object obj = Factory.getObject(num);
System.out.println(obj);*/
//2.静态工厂+反射=增强灵活性与可维护性;且使得代码变为软编码模式(配置文件灵活变更)
//分析:Properties加载配置文件,根据输入的编号,得到value
Properties properties = new Properties();
properties.load(new FileInputStream("factory.properties"));
String className = properties.getProperty(num+""); //获取类名
Class clazz = Class.forName(className);
Object obj = Factory.getObject(clazz);
System.out.println(obj);
}
}
1.2 单例(重点)
单例:
目标:每次获取到的对象都是同一个对象
步骤:
1.通过静态方法返回对象
2.返回的对象是静态属性(静态属性只初始一份)
3.属性私有化
单例中的饿汉式—立即加载的模式
饿汉式
class MySingle{
private MySingle() {
}
private static MySingle single = new MySingle();
public static MySingle getInstance() {
return single; //饿汉式
}
}
public class Test1 {
public static void main(String[] args) {
MySingle single1 = MySingle.getInstance();
MySingle single2 = MySingle.getInstance();
System.out.println(single1==single2);
}
}
懒汉式
//单例中的懒汉式---延时加载的模式
class MySingle2{
private MySingle2() {
}
private static MySingle2 single; //懒汉式
public static MySingle2 getInstance() {
if(single==null) {
single = new MySingle2(); //懒汉式
}
return single;
}
}
public class Test2 {
public static void main(String[] args) {
MySingle2 single1 = MySingle2.getInstance();
MySingle2 single2 = MySingle2.getInstance();
System.out.println(single1==single2);
}
}
懒汉式在多线程中的安全隐患
思考:两种单例模式,在多线程中哪种有安全隐患,请复现并处理—懒汉式
分析:
问题:创建线程类,多线程在run方法中调用getInstance方法;
看看getInstance的if判断中进入了几次,如果有多次,则出现了安全问题
//解决方案:加锁
class MySingle{
private static MySingle single;
public static MySingle getInstance() {
if(single==null) {
//单例懒汉式线程安全提高效率的写法
synchronized ("lock") {
if(single==null) {
System.out.println("实例化对象...");
single = new MySingle();
}
}
}
return single;
}
}
class MyThread extends Thread{
@Override
public void run() {
//T1,T2,T3
MySingle.getInstance();
}
}
public class Test1 {
public static void main(String[] args) {
for(int i=0;i<10;i++) {
new MyThread().start();
}
}
}
2.枚举(了解)
枚举:应用场景与状态值是一致的
在枚举中提供了多个常量值,可以更方便的使用这些常量值进行逻辑判断
选择:
1.如果状态值不多,直接选择静态常量
2.如果状态值很多,可以使用枚举更方便更安全地使用这些常量值
枚举的本质
1.继承Enum的终止类
2.里面的枚举值是静态常量
//枚举应用:
//案例:通过状态值来判断性别
enum MyEnum{
//枚举值
S_MAN,S_WOMAN
}
@Deprecated //过期的注解,定义到类上,类过期;定义到方法上,方法过期
class MyClass{
@Deprecated
public void test() {
}
}
//@interface:标注该注解类中只能有属性
@interface Annotation{
//里面只能写属性
String name(); //定义属性未赋值
int age() default 30; //定义属性并赋值
}
public class Test1 {
public static final int SEX_MAN = 1;
public static final int SEX_WOMAN = 2;
public static void main(String[] args) {
/*int sex = Test1.SEX_MAN;
switch (sex) {
case Test1.SEX_MAN:
System.out.println("你选择的是男性");
break;
case Test1.SEX_WOMAN:
System.out.println("你选择的是女性");
break;
default:
System.out.println("您的输入有误~");
break;
}*/
MyEnum en = MyEnum.S_MAN;
switch (en) {
case S_MAN:
System.out.println("你选择的是男性");
break;
case S_WOMAN:
System.out.println("你选择的是女性");
break;
default:
System.out.println("您的输入有误~");
break;
}
}
}
- Lambda表达式(重点)
概述:特殊的匿名内部类,书写格式参照着匿名内部类来改造即可
用法:可以将lambda表达式(相当于匿名内部类的传参写法)以参数形式传递
lambda语法结构:
接口引用 = (参数)->{}
描述:->左边是匿名内部类中重写方法的参数
->右边是匿名内部类中的函数体及返回值
细节1:如果匿名内部类的重写方法体只有一条语句,return和{}可以省略
细节2:lambda表达式可以通过接口引用自动识别结构
细节3:()中只填写参数名即可;如果只有一个参数,则可省略()
细节4:形参列表为空,则保留()
细节5:lambda表达式不会单独生成内部类文件
3.1 Lambda表达式案例

public class Test1 {
public static void main(String[] args) {
//案例1:线程的创建
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("启动子线程..");
}
}).start();
//Lambda表达式:
new Thread(()->System.out.println("lambda表达式启动")).start();
//案例2:比较器方法
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2; //升序形式
}
};
//lambda表达式:
Comparator<Integer> com2 = (c1,c2)->c1-c2;
}
}
3.2 自定义函数式接口
函数式接口:也是lambda表达式的内容,约定了接口中只有一个方法
//自定义函数式接口: 定义usb接口
@FunctionalInterface //函数式接口注解
interface IUsb{
void connect(); //只能有一个方法
}
public class Test2 {
public static void main(String[] args) {
//匿名内部类
IUsb usb = new IUsb() {
@Override
public void connect() {
System.out.println("匿名内部类调用");
}
};
usb.connect();
//lambda表达式
IUsb usb2 = ()->System.out.println("lambda表达式");
usb2.connect();
}
}
3.3 系统提供的函数式接口
系统定义好的函数式接口
1.消费型接口:Consumer
2.供给型接口:Supplier
3.函数型接口:Function
4.断言型接口:Predicate
分析: 左边接口引用 右边先写匿名内部类,再改造成lambda表达式
public class Test3 {
public static void main(String[] args) {
//-----1.消费型接口:有参数无返回值类型-----
Consumer<Integer> consumer = new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println("花费了"+t+"块钱");
}
};
consumer.accept(600);
Consumer<Integer> consumer2 = m->System.out.println("消费了"+m);
consumer2.accept(800);
//----2.供给型接口:无参数有返回值类型-----
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
return 6; //往往返回随机值
}
};
System.out.println(supplier.get());
Supplier<Integer> supplier2 = ()->new Random().nextInt(8);
System.out.println(supplier2.get());
//----3.函数型接口:有参数有返回值类型-----
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String t) {
return t.length(); //传入字符串,返回长度
}
};
System.out.println(function.apply("hello")); //5
Function<String, String> function2 = t->t.toUpperCase();
System.out.println(function2.apply("hello")); //HELLO
//----4.断言型接口:有参数有返回值类型,返回boolean类型-----
Predicate<Integer> predicate = new Predicate<Integer>() {
@Override
public boolean test(Integer t) {
return t>5;
}
};
System.out.println(predicate.test(6)); //true
Predicate<Integer> predicate2 = t->t>5;
System.out.println(predicate2.test(8)); //true
}
}
3.4 方法引用(了解)
是lambda表达式的简化写法;只不过用在特别的场景,不作为重点
//案例:打印方式的简写
public class Test4 {
public static void main(String[] args) {
//lambda表达式: 可以打印参数以外信息
Consumer<Integer> con = m->System.out.println("消费了"+m);
con.accept(600);
//使用方法引用继续简化: 只能打印参数信息,特定场景使用
Consumer<Integer> con2 = System.out::println;
con2.accept(800);
}
}
- Stream(重点)
Stream:类似于集合,只不过集合是存数据的; Stream只是操作中间过程
Stream使用:
1.创建Stream
2.Stream的中间操作过程
3.Stream的终止操作: 遍历
//1.Stream的创建:
public class Test1 {
public static void main(String[] args) {
//1.通过List集合获取Stream对象
List<String> list = new ArrayList<String>();
list.add("zs");
list.add("ls");
list.add("ww");
//单线程流--有序
list.stream().forEach(System.out::println);
System.out.println("--------------");
//多线程流--无序
list.parallelStream().forEach(System.out::println);
//Arrays方式创建Stream
Arrays.stream(new int[]{
1,3,2}).forEach(System.out::println);
//Stream接口的of方法创建
Stream.of("aa","cc","bb").forEach(System.out::println);
//Stream接口的iterate方法创建: 迭代(参数1:初始值 参数2:在当前值基础上的迭代)
//limit(4):中间过程,限制多少个
Stream.iterate(2, t->t+1).limit(4).forEach(System.out::println);
//Stream接口的generate方法创建
System.out.println("-----");
Stream.generate(()->new Random().nextInt(6)).limit(3).forEach(System.out::println);
//IntStream的of,range,rangeClosed
System.out.println("====of===");
IntStream.of(3,5,8).forEach(System.out::println);
System.out.println("====range:开区间,不包括最后一个===");
IntStream.range(3, 6).forEach(System.out::println);
System.out.println("====range:闭区间,包括最后一个===");
IntStream.rangeClosed(3, 6).forEach(System.out::println);
}
}