Java必须精通第七课

java反射机制

java中较为重要的就是反射机制,那么什么是反射机制呢?举个简单的例子来说,正常情况下,如果已经有一个类,则肯定可以通过类创建对象;那么如果现在要求通过一个对象找到一个类的名称,此时就需要用到反射机制,如果要完成反射操作,则首先应该认识的就是Class类。
在反射的学习中一定要把握一个概念,一切操作都将使用Object完成,类、数组的引用都可以使用Object接收。只有把握了这个概念,才能更清楚的掌握反射机制的作用。

认识Class类

在正常情况下,需要先有一个类的完整路径引用之后才可以按照固定的格式产生实例对象,但是在java中也允许通过一个实例化对象找到一个类的完整信息,那么这就是Class类的功能。

public class Demo{
	public static void main(String[] args){
		Demo demo=new Demo();
		System.out.print(demo.getClass().getName());
	}
}
结果:
myTest.Demo
从结果可以发现,通过一个对象得到了对象所在的完整的“包.类”名称,那么getClass类是从哪里定义的呢,
任何一个类如果没有明确的声明继承自哪个父类时,则默认继承Object类,所以getClass()方法是Object类
中的:
 public final native Class<?> getClass();
 以上方法返回值的类型是一个Class类,实际上此类是java反射的源头。所谓的反射从程序的运行结果来看也
 很好理解,即可以通过对象反射求出类的名称。

反射过程

所有类的对象实际上都是Class类的实例
在java中Object类是一切类的父类那么所有类的对象实际上也就是java.lang.Class类的实例,所以所有对象都可以转变为java.lang.Class类型表示。
Class本身表示一个类的本身,通过Class可以完整的得到一个类中的完整结构,包括此类中方法定义、属性定义等
Class类中常用方法
Class类方法
在Class类中本身没有定义任何的构造方法,所以如果要使用则首先必须通过forName()方法实例化对象,除此之外,也可以使用“类.Class”或“对象.getClass()”方法实例化。

public class Demo{
	public static void main(String[] args){
		Class<?> c1=null;
		Class<?> c2=null;
		Class<?> c3=null;
		try {
			//最常用形式
			c1=Class.forName("myTest.Demo");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		c2=new Demo().getClass();
		c3=Demo.class;
		System.out.println("类的名称为:"+c1.getName());
		System.out.println("类的名称为:"+c2.getName());
		System.out.println("类的名称为:"+c3.getName());
	}
}
结果:
类的名称为:myTest.Demo
类的名称为:myTest.Demo
类的名称为:myTest.Demo
从结果看,三种实例化Class对象的方式是一样的,但是使用forName()静态方法实例化Class对象是比较
常用的,应该重点掌握。
除了forName()方式外,另外两种“对象.getClass()”和“类.class”都需要导入一个明确的类,如果一个类
操作不明确时,使用起来可能会受到一些限制,但是forName传入时,只需要以字符串的方式传入即可,这样
就让程序具备了更大的灵活性,所以此方法是最常见的方法。
Class类的使用

实际上Class类在开发中最常用的用法就是实例化对象的操作,即可以根据一个给定的字符串(此字符串包含了完整的“包.类”的路径)来实例化一个类的对象。

1.通过无参构造实例化对象
public class Person{
	private String name;
	private int age;
	
	public Person() {
		System.out.println("我是构造方法...");
	}
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public boolean equals(Object obj) {
		if(this==obj) {
			return true;
		}
		if(!(obj instanceof Person)) {
			return false;	
		}
		Person p=(Person) obj;
		if(this.name.equals(p.name)&&this.age==p.age) {
			return true;
		}else {
			return false;
		}
	}
	@Override
	public int hashCode() {
		return this.name.hashCode()*this.age;
	}

	@Override
	public String toString() {
		return "姓名:"+this.name+";"+"年龄:"+this.age;
		
	}
}

public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Person p=null;
		try {
			p=(Person) c.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		p.setName("zhangsan");
		p.setAge(15);
		System.out.println(p);
	}
}
结果:
我是构造方法...
姓名:zhangsan;年龄:15

从成宿的运行结果来看,通过Class/forName()方法实例化Class对象之后,直接调用newInstance()方法
就可以根据传入的完整“包.类”名称的方式进行对象的实例化操作,完全取代了之前使用new的操作方式。
但在使用上注意一点,被实例对象的类中必须存在无参构造方法,如果不存在,则肯定无法实例化。

各种高级应用中都提倡类中存在无参的构造方法
在实际的java开发中,反射是最为重要的操作原理,在现在的开发设计中大量的应用了反射处理机制,如
Struts,Spring框架等,在大部分的操作中基本上都是操作无参的构造方法,所以在开发中一定要保留无参
构造方法。

2.调用有参构造方法实例化
如果没有无参构造方法,可以通过其他方式进行实例化操作,只是在操作时需要明确的调用类中的构造方法,
并将参数传递进去之后,才可以进行实例化操作。步骤如下:
(1):通过Class类中的getConstructors()方法,取得本类中全部的构造方法。
(2):向构造方法中传递一个对象数组进去,里面包含了构造方法所需要的各个参数。
(3):之后通过Constructor实例化对象

Constructor常用方法

public class Person{
	private String name;
	private int age;
	
	/*public Person() {
		System.out.println("我是构造方法...");
	}*/
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
		System.out.println("我是有参构造方法...");
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public boolean equals(Object obj) {
		if(this==obj) {
			return true;
		}
		if(!(obj instanceof Person)) {
			return false;	
		}
		Person p=(Person) obj;
		if(this.name.equals(p.name)&&this.age==p.age) {
			return true;
		}else {
			return false;
		}
	}
	@Override
	public int hashCode() {
		return this.name.hashCode()*this.age;
	}

	@Override
	public String toString() {
		return "姓名:"+this.name+";"+"年龄:"+this.age;
		
	}
}


public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Person p=null;
		Constructor<?> cons[]=null;//声明一个构造方法数组
		cons=c.getConstructors();//通过反射取得全部构造方法
			try {
				//因为只有一个构造方法所以数组下标为0
				p=(Person) cons[0].newInstance("lisi",40);
			} catch (InstantiationException | IllegalAccessException 
					| IllegalArgumentException
					| InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		System.out.println(p);
	}
}
结果:
我是有参构造方法...
姓名:lisi;年龄:40
反射的应用-取得类的结构

反射的应用不仅可以实例化,还可以通过反射得到一个类的完整结构,那么这就要使用到java.lang.reflect包一下的几个类
Constructor:表示类中的构造方法
Field:表示类中的属性
Method:表示类中的方法
这三个类都是AccessibleObject类的子类

准备操作:
一个接口类China:
public interface China{
	public static final String NATIONAL="China";
	public static final String ANTHOR="张三";
	public void sayChina();
	public String sayHello(String name,int age);
}

一个实现类Person:
public class Person implements China{
	private String name;
	private int age;
	
	public Person() {
		System.out.println("我是构造方法...");
	}
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
		System.out.println("我是有参构造方法...");
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "姓名:"+this.name+";"+"年龄:"+this.age;
		
	}
	@Override
	public void sayChina() {
		System.out.println("作者:"+ANTHOR+"--"+"国籍:"+NATIONAL);
	}
	@Override
	public String sayHello(String name, int age) {
		// TODO Auto-generated method stub
		return name+",您好,我今年"+age+"岁了。";
	}
}

1.取得所实现的全部接口:
要取得一个类所实现的全部接口,则必须使用Class类中的getInterfaces()方法。
public Class<?>[] getInterfaces()
getInterfaces()方法返回一个Class类的对象数组,之后直接利用Class类中的getName()方法输出即可。

public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Class<?> cla[]=c.getInterfaces();
		for(int i=0;i<cla.length;i++) {
			System.out.println("实现的接口有--"+cla[i].getName());
		}
	}
}
结果:
实现的接口有--myTest.China
因为接口是类的特殊形式,而且一个类可以实现多个接口,所以此时以Class数组的形式将全部的接口对象返回
并利用循环的方式将内容依次输出。

3.取得父类
一个类可以实现多个接口,但只能继承一个父类,所以如果要取得一个类的父类,可以直接使用Class类中
的getSuperClass()方法
public native Class<? super T> getSuperclass()
getSuperClass()方法返回的是Class实例,和之前得到接口一样,可以通过getName()方法取得名称。
public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Class<?> cla=c.getSuperclass();
			System.out.println("父类是--"+cla.getName());
	}
}结果:
父类是--java.lang.Object
因为没有直接继承父类,所以父类是Object

4.取得全部构造方法
public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Constructor<?> cla[]=c.getConstructors();
		for(int i=0;i<cla.length;i++) {
			System.out.println("构造方法是--"+cla[i]);
		}
	}
}
结果:
构造方法是--public myTest.Person()
构造方法是--public myTest.Person(java.lang.String,int)

实例:取得全部构造方法、类型、权限
public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Constructor<?> cla[]=c.getConstructors();
		for(int i=0;i<cla.length;i++) {
			Class<?>[] type=cla[i].getParameterTypes();
			
			System.out.print("构造方法");
			int mo=cla[i].getModifiers();//取出权限
			System.out.print(Modifier.toString(mo)+" ");
			String conStr=cla[i].getName();
			System.out.print(conStr);
			System.out.print("(");
			for(int j=0;j<type.length;j++) {
				System.out.print(type[j].getName()+" arg"+i);
				if(j<type.length-1) {
					System.out.print(",");
				}
			}
			System.out.println("){}");
		}
	}
}
结果:
构造方法public myTest.Person(){}
构造方法public myTest.Person(java.lang.String arg1,int arg1){}

5.取得全部方法
要取得一个类中的全部方法,可以使用Class类中的getMethods()方法,此方法返回一个Method类的对象
数组。如果想进一步取得方法的信息,如方法的参数、异常等,则就必须依靠Method类

Method类常用方法
Method

public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Method m[]=c.getMethods();
		for(int i=0;i<m.length;i++) {
			//得到方法的返回值类型
			m[i].getReturnType();
			//得到全部参数类型
			m[i].getParameterTypes();
			//得到方法的修饰符
			m[i].getModifiers();
			//得到方法的名称
			m[i].getName();
			//得到全部的异常
			m[i].getExceptionTypes();
		}
	}
}
在IDE中输入一个“.”就可以联想到相关的方法。这就是利用反射机制实现的。

6.得到类的全部属性
public Field[] getDeclaredFields() throws SecurityException
以上方法返回的都是Field数组,每一个Field对象表示类中的一个属性,要取得进一步属性的信息就要看
Field的方法。

Field常用方法

public class Demo{
	public static void main(String[] args){
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//或得类中的属性
		Field f[]=c.getDeclaredFields();
		for(int i=0;i<f.length;i++) {
			//获得属性的类型
			f[i].getType();
			//或得属性的修饰字符
			f[i].getModifiers();
		}
		//获得普通代码块的属性
		c.getFields();
	}
}

java反射机制的深入应用

反射除了可以取得一个类的完整结构外,还可以调用类中的指定方法或指定属性,并且可以通过反射完成对数组的操作。

1.通过Class类的getMethod(String name,Class...parameterTypes)方法取得一个Method的对象,
并设置此方法操作时所需要的参数类型。
2.之后才可以使用invoke进行调用,并向方法中传入要设置的参数。
public class Demo{
	public static void main(String[] args) throws Exception{
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			Method mc=c.getMethod("sayChina");
			mc.invoke(c.newInstance());
	}
}
结果:
我是构造方法...
作者:张三--国籍:China

通过Class类中的getMethod()方法,根据一个类中的方法名称取得Method对象,并通过invoke调用指定的
方法,但是在使用invoke方法时,必须传入一个类的实例化对象,因为在sayChina()方法上没有任何的参
数,所以此处没有设置参数类型和参数内容

调用过程

调用有参方法
public class Demo{
	public static void main(String[] args) throws Exception{
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			Method mc=c.getMethod("sayHello",String.class,int.class);
			String rv=null;
			rv=(String) mc.invoke(c.newInstance(),"wangwu",60);
			System.out.print(rv);
	}
}
结果:
我是构造方法...
wangwu,您好,我今年60岁了。

因为sayHello()方法本身要接收两个参数,所以在使用getMethod()方法调用时,除了要指定方法的调用
名称,也同样指定了参数类型,因为sayHello()方法调用之后存在返回值,而且返回值类型是String,所以
使用了一个字符串接收返回值内容。

实例:调用getter和setter方法
一直的概念是“类的属性必须封装,封装之后的属性要通过setter和getter方法”设置取得,下面演示怎么调用
Person类中的setter和getter方法。
public class Person implements China{
	private String name;
	private int age;
	
	public Person() {
		System.out.println("我是构造方法...");
	}
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
		System.out.println("我是有参构造方法...");
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "姓名:"+this.name+";"+"年龄:"+this.age;
		
	}
	@Override
	public void sayChina() {
		System.out.println("作者:"+ANTHOR+"--"+"国籍:"+NATIONAL);
	}
	@Override
	public String sayHello(String name, int age) {
		// TODO Auto-generated method stub
		return name+",您好,我今年"+age+"岁了。";
	}
}

public class Demo{
	public static void main(String[] args) throws Exception{
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Object o=c.newInstance();
		setter(o, "name", "张三", String.class);
		setter(o, "age", 30, int.class);
		System.out.println("姓名:");
		getter(o, "name");
		System.out.println("年龄:");
		getter(o, "age");
	}
	public static void setter(Object obj,String str,Object val,Class<?> type) 
	throws Exception {
		Method m=obj.getClass().getMethod("set"+initStr(str), type);
		m.invoke(obj, val);
	}
	public static void getter(Object obj,String str) throws Exception{
		Method m=obj.getClass().getMethod("get"+initStr(str));
		System.out.println(m.invoke(obj));
	}
	public static String initStr(String old) {
		String str=old.substring(0,1).toUpperCase()+old.substring(1);
		return str;//单词首字母大写
	}
}
结果:
我是构造方法...
姓名:
张三
年龄:
30

实例:通过反射操作属性
在反射操作中,虽然可以使用Method调用类中的setter个getter方法设置和取得属性,但是这样操作毕竟很
麻烦,所以在反射机制中也可以直接通过Field类操作类中的属性,通过Field类提供的set和get方法就可以
完成设置和取得属性的操作,但是在操作前首先注意的是,在类中所有的属性已经设置成私有的访问权限,
所以在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要擦操作的属性
设置成可以 被外部访问。
public class Demo{
	public static void main(String[] args) throws Exception{
		Class<?> c=null;
		try {
			c=Class.forName("myTest.Person");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Object o=c.newInstance();
		Field nameField=null;
		Field ageField=null;
		nameField=c.getDeclaredField("name");
		ageField=c.getDeclaredField("age");
		nameField.setAccessible(true);
		nameField.set(o, "lisi");
		ageField.setAccessible(true);
		ageField.set(o, 14);
		System.out.println("姓名:"+nameField.get(o));
		System.out.println("年龄:"+ageField.get(o));
	}
}
结果:
姓名:lisi
年龄:14
通过反射操作数组

反射机制不仅只能用在类上,还可以用在任意的引用数据类型的数据上,这就包括数组,可以通过以下方式取得一个数组的Class对象。
public native Class<?> getComponentType();
在反射操作包java.lang.reflect中使用Array类表示一个数组,可以通过此类取得数组长度和数组内容的操作。
Array类常用方法

实例:取得数组信息,并修改数组
public class Demo{
	public static void main(String[] args) throws Exception{
		int temp[]= {1,2,3,4};
		Class<?> cl=temp.getClass().getComponentType();
		System.out.println("类型:"+cl.getName());
		System.out.println("长度:"+Array.getLength(temp));
		System.out.println("第一个内容:"+Array.get(temp,0));
		//修改第一个内容
		Array.set(temp, 0, 5);
		System.out.println("第一个内容:"+Array.get(temp,0));
	}
}
结果:
类型:int
长度:4
第一个内容:1
第一个内容:5
动态代理

之前所讲解的代理类属于静态代理,因为每一个代理类只能为一个接口服务,这样一来程序开发中必然产生很多个代理。最好的做法是通过一个代理类完成全部的代理功能。那么此时就必须使用动态代理来完成。
在java中想要实现动态代理机制,则需要java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类的支持。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
在接口中指定义了一个invoke方法,此方法中有三个参数,参数意义如下:
Object proxy:被代理的对象
Method method:要调用的方法
Object[] args:方法调用时所需要的参数。
Proxy是专门完成代理的操作类,可以通过此类为一个和多个接口动态的生成实现类。
Proxy类提供了如下方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
通过newProxyInstance可以动态的生成实现类,此方法参数意义如下:
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler 接口子类的实例

类加载器
在Proxy类的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在java中主要有三种加载器:
Bootstrapt ClassLoader:此加载器采用C++编写,一般开发中是看不到的。
Extension ClassLoader:用来进行拓展类的加载,一般对应的是jre\lib\ext目录中的类。
AppClassLoader:加载classpath指定的类,是最常用的一种加载器。

实例:取得加载器
public class Demo{
	public static void main(String[] args) throws Exception{
		Person p=new Person();
		String cl=p.getClass().getClassLoader().getClass().getName();
		System.out.println(cl);
	}
}
结果:
我是构造方法...
sun.misc.Launcher$AppClassLoader
从结果来看,默认的ClassLoader就是AppClassLoader


如果要完成动态代理,首先定义一个InvocationHandler接口的子类,已完成代理的具体操作。
public interface China{
	public static final String NATIONAL="China";
	public static final String ANTHOR="张三";
	public void sayChina();
	public String sayHello(String name,int age);
}

public class Person implements China{
	private String name;
	private int age;
	
	public Person() {
		System.out.println("我是构造方法...");
	}
	public Person(String name,int age) {
		this.name=name;
		this.age=age;
		System.out.println("我是有参构造方法...");
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "姓名:"+this.name+";"+"年龄:"+this.age;
		
	}
	@Override
	public void sayChina() {
		System.out.println("作者:"+ANTHOR+"--"+"国籍:"+NATIONAL);
	}
	@Override
	public String sayHello(String name, int age) {
		// TODO Auto-generated method stub
		return name+",您好,我今年"+age+"岁了。";
	}
}

public class MyInvoHandler implements InvocationHandler{
	private Object obj;
	public Object bind(Object obj) {
		this.obj=obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), 
				obj.getClass().getInterfaces(), this);
	} 
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) 
	throws Throwable {
		Object temp=method.invoke(this.obj, args);
		return temp;
	}
}

public class Demo{
	public static void main(String[] args) throws Exception{
		MyInvoHandler myInvoke=new MyInvoHandler();
		China china=(China) myInvoke.bind(new Person());
		String str=china.sayHello("老师", 7);
		System.out.println(str);
	}
}
结果:
我是构造方法...
老师,您好,我今年7岁了。

完成的功能与之前的静态代理没什么不同,所以在一般的开发中很少会使用这种动态代理的机制,但在编写一些
底层代码或者是一些框架(如Spring Framework)时这种动态代理就比较常用了。
类的声明周期

在一个类编译完成之后,下一步就要开始使用类,如果要使用类肯定离不开JVM。在程序执行中,JVM通过装载、链接、初始化3个步骤完成 。类的装载就是通过类的加载器,把.Class二进制文件装入JVM方法区,并在堆区创建描述该类的java.lang.Class对象,用来封装数据。注意:同一个类只会被JVM加载一次。
链接就是把二进制数据组装成可运行状态。
链接分为:校验、准备、解析3个步骤,校验是用来确认此二进制文件是否适合当前的JVM版本,准备就是为静态成员分配内存空间,并设置默认值。解析指的是转换常量池的代码引用为直接引用的过程,直到所有的符号引用都可被运行程序使用,(建立完整的对应关系),完成之后类型即可初始化,初始化之后类的对象就可以正常使用,直到一个对象不再使用之后,将被垃圾回收,释放空间。当没有任何引用指向Class对象时,将被卸载,结束类的声明周期。
类及对象的生命周期

工厂设计模式

将反射应用在工厂设计模式中
工厂设计模式在实际开发中使用的非常多,通过简单的工厂设计模式可以达到类的解耦目的,但是之前的工厂设计模式依然存在问题,那就是增加一个子类时,都需要修改工厂类,现在通过反射机制来修改工厂类让其在增加子类时可以不做任何修改,就达到功能补充。

使用反射完成工厂设计
创建一个水果接口
interface Fruit {
	public void eat();
}
创建实现类苹果
public class Apple implements Fruit{
	@Override
	public void eat() {
		System.out.println("...吃苹果");
	}
}
创建实现类橘子
public class Orange implements Fruit{
	@Override
	public void eat() {
		System.out.println("...吃橘子");
	}
}
通过反射创建工厂类
class Factory{
	public static Fruit getInstance(String className) throws Exception {
		Fruit fruit=null;
		fruit=(Fruit) Class.forName(className).newInstance();
		return fruit;
	}
}
动态传入类型,通过工厂调用
public class Demo{
	public static void main(String[] args) throws Exception{
		Fruit f=Factory.getInstance("myTest.Apple");
		if(f!=null) {
			f.eat();
		}
	}
}
结果:
...吃苹果

实例:结合属性文件的工厂模式
以上操作代码虽然可以通过反射取得接口实例,但是在操作时需要传入完整的包.类名称,而且用户也无法知道
一个接口有多少个可以使用的子类,所以此时可以通过属性文件的形式配置所要的子类信息
就是把所有的子类放到属性文件中,set的放到属性文件中,在工厂使用时get出来

总结要点:
1.Class类是反射机制操作的源头
2.Class类的对象有三种实现方式
(1)通过Object类中的getClass()方法
(2)通过“类.Class”形式
(3)通过“Class.forName()”方法,这种方式最常见
3.可以通过Class类中的newInstance()方法进行对象的实例化操作,但是要求类必须存在无参构造方法,如果类中没有无参的构造方法,则必须使用Constructor类完成对象的实例化操作。
4.可以通过反射取得一个类所继承的父类、实现的接口、类中的全部构造方法、全部普通方法及全部属性。
5.使用反射机制可以通过Method调用类中的方法,也可以直接操作类中的属性。
6.动态代理可以解决开发中代理类过多的问题,提供统一的代理实现功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《21天学通Java-第7版 入门到精通》是由林信良编著的一本Java编程教材。这本书主要适合初学者学习Java编程语言。 首先,本书介绍了Java编程的基础知识,包括Java环境的安装和配置、Java基本语法、面向对象编程等。读者可以通过丰富的实例和案例,掌握Java编程中的基本概念和技巧。 其次,本书还详细介绍了Java的高级特性,如异常处理、IO操作、多线程编程、网络编程等。这些内容不仅能帮助读者进一步提高Java编程的能力,还能够应用于实际项目开发中。 此外,书中还介绍了Java的GUI编程、数据库编程以及JavaWeb开发等内容。这使得读者可以在学习完基础知识后,进一步深入学习Java编程的各个方向。 这本书的特点是结构清晰,内容丰富,既适合作为Java编程的入门教材,也适合作为进阶学习的参考。书中还提供了大量的练习题和案例,读者可以通过实际动手编写代码来巩固所学知识。 总之,《21天学通Java-第7版 入门到精通》是一本很好的Java编程教材,无论是初学者还是有一定编程基础的读者,都可以通过学习这本书快速掌握Java编程技能。 ### 回答2: 《21天学通Java-第7版 入门到精通PDF》是一本专门介绍Java编程语言的书籍。Java是一种跨平台的高级编程语言,广泛应用于各种类型的软件开发中。 这本书的第7版涵盖了从入门到精通的内容,适合初学者和有一定基础的读者。书中的内容分为21个章节,每天学习一个章节,需要大约三周的时间。通过阅读本书,读者可以系统地掌握Java的语法、基本概念和编程技巧。 本书的特点是结合理论和实践,通过丰富的实例和练习,帮助读者加深对Java的理解。每个章节都有清晰的学习目标和总结,讲解内容简洁明了,易于理解。此外,书中还介绍了Java的常见开发工具和常用的第三方库,帮助读者提高编程效率。 值得一提的是,本书提供了附带学习资源,包括源代码、习题答案和额外的学习资料。读者可以通过这些资源进行实践和进一步学习,巩固所学知识。 总的来说,《21天学通Java-第7版 入门到精通PDF》是一本适合初学者入门学习Java的好书。通过认真阅读和实践,读者可以逐步掌握Java编程的基本技能,并逐渐提升到精通的水平。这本书是学习Java编程的良好起点,也是提升编程能力的重要工具。 ### 回答3: 《21天学通Java-第7版 入门到精通》是一本针对Java编程语言入门学习的教材。它是以21天为单位进行组织的,每天学习一个特定的主题,帮助读者逐步掌握Java编程的基础知识和技术。 这本书以简明易懂的方式介绍了Java的基本语法、数据类型、运算符、控制结构、方法等基础知识,并通过实例解析帮助读者理解和掌握。每天的学习内容都有相应的练习题,读者可以通过动手实践加深对知识点的理解,并培养编程能力。 《21天学通Java-第7版 入门到精通》不仅涵盖了Java的基础知识,还介绍了面向对象编程、异常处理、多线程编程、GUI编程、数据库编程等高级主题。通过学习这本书,读者可以系统地了解Java的核心特性和常用类库,为进一步深入学习和应用Java打下坚实的基础。 此外,这本书还涵盖了一些实际应用和项目实践,帮助读者将学到的知识应用到实际开发中。它介绍了如何使用Eclipse和Java开发工具进行开发,以及如何使用Java编写Web应用程序和Android应用程序。 总之,如果你想从零开始学习Java编程,这本《21天学通Java-第7版 入门到精通》是一个很好的选择。它不仅具备全面的内容,而且以简单易懂的方式呈现,适合初学者快速上手。无论是自学还是作为教材,这本书都能帮助你掌握Java编程技能,成为一名优秀的Java开发人员。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值