Java之反射
反射:
反射之中的所有核心操作都是通过Class类展开的,可以说Class类是反射的根源所在,那么获取Class类的实例化对象,可以采用三种方式实现;
首先观察java.lang.Class类的定义:
【JDK1.0】:public final class Class extends Object implements Serializable,GenericDeclaration, Type, AnnotatedElement;
从JDK1.5开始,Class类的定义可以使用泛型进行标记,这样的用法主要是希望可以避免所谓的向下转型;
通过三种操作观察三种实例化形式:
【Object类支持】:Object类可以根据实例化对象获取Class对象:
但是这种方式有一个不是缺点的缺点:如果现在只是想获取Class类对象,则必须产生指定类后才能获取。
public final Class<?> getClass();
class Person{
}
public class MAIN {
public static void main(String[] args) {
Person pr = new Person(); // 已经存在有指定类的实例化对象
Class<?> cls = pr.getClass();
System.out.println(cls.getName()); // 获取的是类的完整名称
}
}
【JVM直接支持】:采用“类.Class”直接实例化:
- 特点:如果是使用这种方式,那么必须导入程序对应的开发包;
class Person{
}
public class MAIN {
public static void main(String[] args) {
Class<?> cls = Person.class;
System.out.println(cls.getName()); // 获取的是类的完整名称
}
}
【Class类支持】:在Class类中有一个static方法:
- 采用的是字符串的形式定义要使用的类型,并且程序中不需要编写任何的import语句;
public static Class<?> forName?(String className) throws ClassNotFoundException;
package Demo_2_27_反射1_Person
|
package Demo_2_27_反射1
|
public class Person {
}
|
public class MAIN {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("Demo_2_27_反射1_Person.Person");
System.out.println(cls.getName()); // 获取的是类的完整名称
}
}
|
如果此时要使用的程序类不存在,那么会出现“ClassNotFoundException”的异常。
反射实例化对象:
获取了Class对象的最大意义实际上并不是在于只是对象的实例化操作形式,更重要的是Clss类里面提供有一个对象的实例化方法(代替了关键字new):
- 在JDK1.9之前的实例化:
@Deprecated(since="9") public T newInstance() throws InstantiationException, IllegalAccessException;
- 在JDK1.9之后:
clazz.getDeclaredConstructor().newInstance();
通过newInstance()方法实例化Person类对象:
package Demo_2_27_反射1_Person
|
package Demo_2_27_反射1
|
public class Person {
// 任何情况下实例化对象都要调用类的构造方法
public Person() {
System.out.println("******* Person类构造方法 **********");
}
@Override
public String toString() {
return "我是Person类!";
}
}
|
public class MAIN {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("Demo_2_27_反射1_Person.Person");
Object obj = cls.newInstance();// 实例化对象,JDK1.9之后被废除
System.out.println(obj); // 输出对象调用toString()方法
}
}
输出结果: 【cls.newInstance()等价于关键字new】:******* Person类构造方法 ********** 【System.out.println(obj);】:我是Person类! |
使用newInstance()方法依然要调用类的无参构造方法,其本质等价于“类 对象 = new 类()”,相当于隐含了关键字new,而直接使用字符串进行替代。
JDK1.9之后newInstance()被替代了:
public class MAIN {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("Demo_2_27_反射1_Person.Person");
Object obj = cls.getDeclaredConstructor().newInstance();
System.out.println(obj); // 输出对象调用toString()方法
}
}
因为默认的Class类中的newInstance()方法只能够调用无参构造,被认为描述不正确,所以将其变换了形式。
反射与工厂设计模式:
静态工厂类:
工厂模式是避免客户端直接与类进行交集,通过返回接口的方式进行安全使用;
interface IMessage {
public void send();
}
class NetMessage implements IMessage {
@Override
public void send() {
System.out.println("发送网络消息!!");
}
}
class Factory {
private Factory() {
} // 工厂类不需要进行实例化,所以进行构造方法私有化
public static IMessage getInstance(String className) { // 因为不会实例化,所以方法都使用static修饰,方便调用
if ("NetMessage".equals(className)) { // 传入的类名称判断
return new NetMessage();
}
return null;
}
}
public class MAIN {
public static void main(String[] args) {
IMessage msg = Factory.getInstance("NetMessage");
assert msg != null;
msg.send();
}
}
以上的工厂设计模式属于静态工厂模式,最大的缺点就是,当需要追加一个接口的子类的时候,那么工厂类必须进行修改。
如果现在再追加一个子类class CloudMessage:
class CloudMessage implements IMessage{
@Override
public void send() {
System.out.println("云消息!!");
}
}
动态工厂类:
那么现在的工厂类中的判断就必须进行修改:
class Factory {
private Factory() {
} // 工厂类不需要进行实例化,所以进行构造方法私有化
public static IMessage getInstance(String className) { // 因为不会实例化,所以方法都使用static修饰,方便调用
if ("NetMessage".equals(className)) { // 传入的类名称判断
return new NetMessage();
}else if ("CloudMessage".equals(className)){
return new CloudMessage();
}
return null;
}
}
如果不增加一个对CloudMessage的判断对象获取,那么将无法获取CloudMessage的实例化对象。
随着接口的使用,可能就会产生越来越多的子类,那么这样的修改是会让人无法接受的,所以就需要进行改进为另一种方式。
这种时候最好的解决方案就是不适用关键字new来完成,因为关键字new的使用需要一个明确的类存在。而newInstance()的方法只需要一个表示类名称的字符串即可应用。
现在对工厂类进行修改:
package Demo_2_27_反射与工厂设计模式;
interface IMessage {
public void send();
}
class NetMessage implements IMessage {
@Override
public void send() {
System.out.println("发送网络消息!!");
}
}
class CloudMessage implements IMessage{
@Override
public void send() {
System.out.println("云消息!!");
}
}
class Factory {
private Factory() {
} // 工厂类不需要进行实例化,所以进行构造方法私有化
public static IMessage getInstance(String className) { // 因为不会实例化,所以方法都使用static修饰,方便调用
IMessage instance = null;
try {
instance = (IMessage) Class.forName(className).getDeclaredConstructor().newInstance(); // 使用反射机制获取子类对象
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
}
public class MAIN {
public static void main(String[] args) {
IMessage msg = Factory.getInstance("Demo_2_27_反射与工厂设计模式.CloudMessage");
assert msg != null;
msg.send();
}
}
此时再进行子类对象的追加的时候,工厂类都不会进行任何的修改。
静态和动态都是通过名称寻找,不同的是静态需要先确保子类存在并能通过识别判断,而动态则只需要确保子类存在即可,因为反射的机制会自动去寻找存在的子类并创建实例化对象。
思考(多接口的工厂模式):
此时的工厂类已经看似完善,但是在实际情况中,存在大量的接口,并且这些接口都可能通过工厂类进行实例化,所以工厂类不应该只是为某一个类进行服务,而是应该为所有的接口进行服务。
此时工厂类就需要再进行接口的泛型化:
interface IMessage {
public void send();
}
interface IService{
public void service();
}
class HouseService implements IService {
@Override
public void service() {
System.out.println("【住宿服务】");
}
}
class NetMessage implements IMessage {
@Override
public void send() {
System.out.println("【发送网络消息】");
}
}
class CloudMessage implements IMessage{
@Override
public void send() {
System.out.println("【云消息】");
}
}
class Factory {
private Factory() {
} // 工厂类不需要进行实例化,所以进行构造方法私有化
@SuppressWarnings("unchecked") // 强制转型的时候会有警告,进行压制即可
public static T getInstance(String className,Class cls) { // 传入的参数1:接口的子类,包.类名称;传入的参数2:接口的类型
T instance = null;
try {
instance = (T) Class.forName(className).getDeclaredConstructor().newInstance(); // 使用反射机制获取子类对象
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
}
public class MAIN {
public static void main(String[] args) {
IMessage msg = Factory.getInstance("Demo_2_27_反射与工厂设计模式.CloudMessage",IMessage.class);
msg.send();
IService sv = Factory.getInstance("Demo_2_27_反射与工厂设计模式.HouseService",IService.class);
sv.service();
}
}
此时的工厂类不再受限于子类和接口追加。
未完待续。。。