反射与设计模式

一、反射应用

调属性

class Person{
	private String name;
	private int    age;
}
public class Test1 {
	public static void main(String[] args) throws Exception{
		//通过反射对象调属性:
		//1.获取类对象
		Class c1 = Person.class;
		//2.获取Field对象
		Field field = c1.getDeclaredField("name");
		//3.给定权限,即可调用私有属性
		field.setAccessible(true);
		//4.field调用set方法进行赋值
		Object o = c1.newInstance();
		field.set(o, "张三丰");
		
		//验证数据是否存进去:get
		System.out.println(field.get(o));
		//System.out.println(((Person)o).name);
	}
}

调方法

class Student {
	public void hello(String name,int age) {
		System.out.println("姓名:"+name+";年龄:"+age);
	}
	public void test() {
	}
}
public class Test2 {
	public static void main(String[] args) throws Exception {
		//类对象调方法:
		//1.获取类对象
		Class c1 = Student.class;
		//2.获取Method对象
		Method method = c1.getMethod("hello", String.class,int.class);
		//3.调用invoke方法
		method.invoke(c1.newInstance(), "zs",33);
	}
}

应用场景

反射应用,往往可以灵活动态的调用属性,方法,构造方法

在程序中,使用了反射,可以使程序的维护性更强

//反射应用场景:灵活获取对象及属性资源
//案例:灵活动态地获取对象,例如:调指定方法返回Dog对象,或返回Cat
class Cat{
	String name;
	@Override
	public String toString() {
		return "Cat [name=" + name + "]";
	}
}
class Dog{
	String name;
	@Override
	public String toString() {
		return "Dog [name=" + name + "]";
	}
}
class Factory{
	public final static int F_DOG=1;
	public final static int F_CAT=2;
	
	public static Dog getDog() {
		return new Dog();
	}
	public static Cat getCat() {
		return new Cat();
	}
	public static Object getObject(int f) {
		if(f==F_DOG) { //违背ocp原则:对内修改的代码要持关闭状态
			return new Dog();
		}else if(f==F_CAT) {
			return new Cat();
		}else {
			return null;
		}
	}
	//泛型方法定义:<T>
	public static <T> T getObject(Class<T> c1,String name) throws Exception {
		Field field = c1.getDeclaredField("name");
		field.setAccessible(true);
		T t = c1.newInstance();
		field.set(t, name);
		return t;
	}
}
public class Test3 {
	public static void main(String[] args) throws Exception {
		//1.面向对象方式获取对象:
		//问题:如果获取30个不同对象,则需要30个方法; 冗余代码太多
		Dog dog = Factory.getDog();
		Cat cat = Factory.getCat();
		
		//2.传入标记获取对象(调一个方法)
		//问题:违背ocp原则;耦合性太强; 维护性太差
		Dog d = (Dog)Factory.getObject(Factory.F_DOG);
		Cat c = (Cat)Factory.getObject(Factory.F_CAT);
		
		//3.反射用法
		Dog dd = Factory.getObject(Dog.class,"旺财");
		Cat cc = Factory.getObject(Cat.class,"加菲猫");
		System.out.println(dd+"---"+cc);
	}
}

二、设计模式

概述:反复被使用的一套设计标准;也可以理解为特点问题的固定解决方案。

好处:可读性,复用性,维护性更强

分类:23种设计模式,总共分3类

对象型模式:针对对象的获取 例如:单例,工厂

结构型模式:针对对象的组织结构 例如:装饰者,代理

行为型模式:针对对象的行为跟踪 例如:观察者

三、工厂模式

概述:从工厂类中根据标记获取不同的对象

//动物的工厂类; 里面获取出不同的对象
class Factory{
	public static final int F_DOG=1;
	public static final int F_CAT=2;
	public static Object getObject(int f) {
		if(f==F_DOG) {  //违背ocp原则
			return new Dog();
		}else if(f==F_CAT) {
			return new Cat();
		}else {
			return null;
		}
	}
	//泛型方法:
	public static <T> T getObject(Class<T> c) throws Exception {
		return c.newInstance();
	}
}
class Dog{}
class Cat{}
public class Test1 {
	public static void main(String[] args) throws Exception {
		//1.基本工厂设计模式:
		//a.静态工厂: 调静态方法(常用)
		//b.实例工厂: 实例化工厂对象调成员方法
		Dog dog = (Dog) Factory.getObject(Factory.F_DOG);
		
		//2.工厂+反射+配置文件
		//“约定大于配置(约定规则取代配置-框架常用);配置大于编码(硬编码->软编码)”
		Properties p = new Properties();
		p.load(new FileInputStream("factory.properties"));
		String path = p.getProperty("1"); //key已知;value变为的
		System.out.println(Factory.getObject(Class.forName(path)));
	}
}

四、单例

概述:每次获取到的对象都是同一个对象

单例设计:

1.类名调用静态方法; 2.返回静态属性 3.构造方法私有化

好处:节省内存资源

单例分类

//单例设计的分类: 饿汉式,懒汉式
class MySingle{
	//private static final MySingle single = new MySingle();  //饿汉式(常用)
	private static MySingle single;   //懒汉式:调用方法时才去new对象
	
	private MySingle() {}
	
	public static MySingle getInstance() {
		if(single==null) {
			single = new MySingle();
		}
		return single;
	}
}
public class Test1 {
	public static void main(String[] args) {
		//单例:
		MySingle my = MySingle.getInstance();
		MySingle my2 = MySingle.getInstance();
		System.out.println(my==my2);  //true
	}
}

懒汉隐患

class MySin{
	private static MySin sin; 
	private MySin() {}
	
	public static MySin getInstance() {
		//隐患问题:共享数据sin,有复合操作---安全隐患
		if(sin==null) { //完整的线程安全处理--性能高
			synchronized("lock") {
				if(sin==null) {
					System.out.println("------");
					sin = new MySin();
				}
			}
		}
		return sin;
	}
} 
public class Test2 {
	public static void main(String[] args) {
		for(int i=1;i<=10;i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					MySin.getInstance();
				}
			}).start();
		}
	}
}

五、枚举

概述:枚举和静态常量类似,都是作为状态值来使用;

当状态值设置的比较少,可以选择静态常量;状态值设置的较多,则选择枚举(写法上更;简洁)

枚举的定义: 创建枚举类后,里面可以生成很多枚举值;这些枚举值的维护性较好

//枚举的本质:继承Enum的最终类   枚举值则是封装后的静态常量
enum MyEnum{  //创建枚举类
	M_DOG,M_CAT
}
class Factory{
	//public static final int F_DOG=1;  //静态常量
	//public static final int F_CAT=2;
	public static Object getObject(MyEnum f) {
		if(f==MyEnum.M_DOG) {  //违背ocp原则
			return new Dog();
		}else if(f==MyEnum.M_CAT) {
			return new Cat();
		}else {
			return null;
		}
	}
}
class Dog{}
class Cat{}
public class Test1 {
	public static void main(String[] args) throws Exception {
		//1.基本工厂设计模式:
		Dog dog = (Dog) Factory.getObject(MyEnum.M_DOG);
	}
}

六、注解

概述:注解可以参与程序代码中的执行;往往在框架中可以取代相关配置信息

回顾使用过的注解:@Test(单元测试-功能型注解) @Override(重写注解-检测型注解)

@interface注解类: 定义当前类中只能使用属性

注解回顾

//回顾注解的用法:
class Animal{
	public void bark() {}
}
@Deprecated //定义该类为过时的类
class Dog extends Animal{
	
	@Deprecated //定义该方法是过时的方法
	@Override  //检测型注解-检测是否为重写方法
	public void bark() {}
}
public class Test1 {
	public static void main(String[] args) {
	}
	@Test //JVM会生成Test1类的对象调成员方法
	public void show() {
		System.out.println("xxxx");
	}
}

注解类

enum MyEnum{
	A,B
}
@interface MyAnno{}
//@interface注解类:定义当前类中只能使用属性
@interface MyAnnotation{
	int age() default 66;    //定义int类型属性
	String name();           //定义String类型
	Class  c1();             //类对象类型
	MyEnum enum1() default MyEnum.A;  //枚举类型
	MyAnno anno();
}
public class Test2 {
	public static void main(String[] args) {
	}
}

七、Lambda

概述:Lambda是jdk8提供的新特性;就是匿名内部类的简写;能用匿名内部类,则可使用lambda表达式

匿名内部类往往常用于接口实现多态的方式中,所以lambda表达式语法也类似,需要有接口引用

语法:

接口引用 = (重写方法参数)->{重写方法体}

应用

//案例:
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
};
Thread th = new Thread(run);
th.start();

//lambda表达式
Runnable run2 = ()->System.out.println("lambda表达式");
Thread th2 = new Thread(run2);
th2.start();

函数式接口

函数式接口也是lambda表达式的应用场景,只不过接口有特殊定义:接口中的抽象方法只有一个

//函数式接口的应用:
//案例:飞天娃具有飞天的能力
@FunctionalInterface  //函数式接口的注解
interface Flyable{
	void fly();  //函数式接口只能有一个抽象方法
}
public class Test2 {
	public static void main(String[] args) {
		new Flyable() {
			@Override
			public void fly() {
				System.out.println("匿名的飞天能力");
			}
		}.fly();
		
		//lambda表达式
		Flyable ff = ()->System.out.println("lambda的飞天能力");
		ff.fly();
		
		//lambda细节:
		//1.根据接口引用可以自动识别lambda表达式结构及调用重写方法
		//2.重写方法体中如果只有一句,则可省略{}以及省略return
		//3.如果只有一个参数,则可省略()
		//4.lambda不能生成独立的内部类文件
	}
}

系统提供的函数式接口

//函数式接口主要有4个:消费型接口,供给型,函数型,断言型
//消费型接口:有参数无返回值方法
//供给型接口:无参数有返回值方法
//函数型接口:有参数有返回值方法-参数和返回值只要是引用类型即可
//断言型接口:有参数有返回值方法-返回类型为boolean类型
//消费型接口:Consumer
Consumer<Integer> con = s->System.out.println("消费了--"+s);
con.accept(666);

//供给型接口:Supplier
Supplier<Integer> sup = ()->new Random().nextInt(6);
System.out.println(sup.get());

八、总结与作业

总结

作业

1. 自定义Person类,并给定一个带参构造, 当Person类的反射对象调用newInstance时会报错,写个例子演示,并说明为什么?

2. 有一个电脑类和手机类都有一个brand属性,在工厂类的静态方法中,传入反射对象及属性值,灵活获取到对应的实体对象,返回的对象中包含属性值

3、配置文件中user.properties中有一组键值对为: key=xx.xx.xx.Person
    请加载配置文件后,根据key获取出value,并通过反射机制获取对应的实体对象

晨考

请写出单例的懒汉式和饿汉式代码,并注释标明:
class L{
  private static final L e = new L();//饿汉
  private static L l;//懒汉
  private L(){}
  public static L getInstance{
    if(l == null){
      l = new L();
    }else{
      return l;
    }
  } 
  
}