反射
- 类加载
概念:
类加载的时机与过程:类是编译器编译后的产物,是硬盘中的一个.class文件,如果在一个java程序中需要使用一个类,JVM必然会从硬盘中加载(读取)该类的信息(.class)文件。
注意:类加载只会执行一次。1. 当JVM第一次使用某个类时,需要通过class_path找到该类的.class文件 2. 将.class文件中描述的信息(属性、方法、构造方法、父类、接口、代码),读取到虚拟机内存中。 3. 加载时会初始化静态资源(静态属性,静态方法)并执行静态初始化代码块。
- 反射的概念:
当JVM从硬盘中加载了一个.class文件的信息会在内存中生成一个对应的Class对象 Class对象就包含了.class文件的所有信息。 面向对象编程:创建对象--->调用方法--->使用属性---->完成功能。 反射编程(上帝模式):通过Class对象---->获取方法--->获取属性---->选择对象执行或赋值。
1 Class对象
- 概念:JVM类加载时的产物,包含了一个类的所有信息。
1.1 获取Class对象的三种方式
- 通过Object中的getClass方法
获取ArrayList类的Class对象。List list = new ArrayList(); Class cls1 = list.getClass();
- 通过类名.class获取Class对象
获取ArrayList类的Class对象。Class<ArrayList> cls2 = ArrayList.class;
- 通过字符串获取Class对象
获取ArrayList类的Class对象。
注意:处理异常Class cls3 = Class.forName("java.util.ArrayList");
1.2 常用方法
- 演示的代码如下:
演示的代码如下:// 通过字符串获取Class对象 Class cl = Class.forName("java.util.ArrayList"); // 获取全类名 String className = cl.getName(); System.out.println(className); // 获取类名 String name = cl.getSimpleName(); System.out.println(name); // 获取直接父类 Class supClass = cl.getSuperclass(); System.out.println(supClass); // 获取实现的接口 Class[] is = cl.getInterfaces(); System.out.println(Arrays.toString(is)); // 获取所有属性(公开的属性或父类继承的公开的属性) Field[] fs = cl.getFields(); System.out.println(Arrays.toString(fs)); // 获取所有属性(本类的所有的) Field[] fs2 = cl.getDeclaredFields(); System.out.println(Arrays.toString(fs2)); // 获取所有的方法(公开的或父类继承的) Method[] ms = cl.getMethods(); System.out.println(Arrays.toString(ms)); // 获取所有的方法(本类的所有的) Method[] ms2 = cl.getDeclaredMethods(); // 获取类中的公开的内部类 Class[] css = Super.class.getClasses(); System.out.println(Arrays.toString(css)); // 获取包 Package p = cl.getPackage(); System.out.println( p ); // 获取构造方法 Constructor[] cts = cl.getConstructors(); System.out.println(Arrays.toString(cts));
package com.txw.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class TestClass {
public static void main(String[] args) throws Exception{
/* // 1.Object中的getClass方法获取
List list = new ArrayList();
Class cls1 = list.getClass();
// 2.通过类名.class获取Class对象
Class<ArrayList> cls2 = ArrayList.class;*/
// 3.通过字符串获取Class对象
Class cl = Class.forName("java.util.ArrayList");
// 获取全类名
String className = cl.getName();
System.out.println(className);
// 获取类名
String name = cl.getSimpleName();
System.out.println(name);
// 获取直接父类
Class supClass = cl.getSuperclass();
System.out.println(supClass);
//获取实现的接口
Class[] is = cl.getInterfaces();
System.out.println(Arrays.toString(is));
// 获取所有属性(公开的属性或父类继承的公开的属性)
Field[] fs = cl.getFields();
System.out.println(Arrays.toString(fs));
// 获取所有属性(本类的所有的)
Field[] fs2 = cl.getDeclaredFields();
System.out.println(Arrays.toString(fs2));
// 获取所有的方法(公开的或父类继承的)
Method[] ms = cl.getMethods();
System.out.println(Arrays.toString(ms));
// 获取所有的方法(本类的所有的)
Method[] ms2 = cl.getDeclaredMethods();
// 获取类中的公开的内部类
Class[] css = Super.class.getClasses();
System.out.println(Arrays.toString(css));
// 获取包
Package p = cl.getPackage();
System.out.println( p );
// 获取构造方法
Constructor[] cts = cl.getConstructors();
System.out.println(Arrays.toString(cts));
}
}
class Super{
public class InnerClass{
}
}
2 反射编程步骤
2.1 获取Class对象
- 获取Class对象,根据Class对象创建对应的实例。
比如:Class对象引用.newInstance();
Class cls = Class.forName("com.txw.Student"); // 获取Class对象 Object obj = cls.newInstance(); // 创建Student对象
2.2 获取属性或方法,选择对象执行
-
获取方法,选择对象执行。
// 获取公开的方法 Method m = cls.getMethod("方法名",参数类型.class,参数类型.class...); // 选择对象执行方法 m.invoke( obj,实参1,实参2...);
-
获取私有方法。
Method m = cls.getDeclaredMethod("方法名",参数类型.class,参数类型.class....); m.setAccessible(true); // 授权访问私有成员 m.invoke(...);
-
获取属性,为某个对象的属性赋值。
// 获取公开属性 Field f = cls.getField("属性名"); // 赋值基本数据类型 f.setInt/setDouble等等(obj,"值"); // 对象类型 f.set(obj,值);
-
获取私有属性,为某个对象的属性赋值。
Field f = cls.getDeclaredField("属性名"); f.setAccessible(true); // 授权访问私有成员 f.set(...);
演示的代码如下:
package com.txw.test;
import java.lang.reflect.Field;
import java.util.Map;
public class TestReflect {
public static void main(String[] args) throws Exception{
// Class cls = Class.forName("com.txw.test.MyClass");
// Object obj = cls.newInstance();
// 面向对象编程
// MyClass mc = new MyClass();
// mc.m1();
// Hello h = new Hello()
// h.m1();
// 反射编程
/* Method m1Method = cls.getMethod("m1"); // 获取无参m1
Method m1Method2 = cls.getMethod("m1",int.class); // 获取有参m1 参数类型为int
m1Method.invoke(obj);
Object result = m1Method2.invoke(obj,10);
System.out.println(result);*/
// Method m = cls.getDeclaredMethod("m2");
// m.setAccessible(true); // 授权访问私有成员
// m.invoke(obj);
// Field fa =cls.getField("a");
// fa.set(obj,100);
// System.out.println(obj);
/* Field fb = cls.getDeclaredField("b");
fb.setAccessible(true); // 授权访问私有成员
fb.set(obj,"Hello");
System.out.println(obj);*/
Class cls = Class.forName("java.util.HashMap");
Object obj = cls.newInstance();
Map map = (Map)obj;
map.put("A","AA");
map.put("B","BB");
System.out.println(map.size());
Field f = cls.getDeclaredField("size");
f.setAccessible(true); // 设置访问私有属性权限
f.setInt(obj,100);
System.out.println(map.size());
map.put("C","CC");
System.out.println(map.size());
System.out.println( map );
}
}
class Hello{
public void m1(){
System.out.println("Hello m1()");
}
}
class MyClass{
public Integer a;
private String b;
public String toString(){
return "MyClass["+b+"]";
}
public void m1(){
System.out.println("MyClass m1()");
}
public String m1(int n){
System.out.println("MyClass m1(int)" + n);
return "invoke success";
}
private void m2(){
System.out.println("private m2()");
}
}
3 单例模式
- 概念:保证应用中的某一类只有一个实例(对象),通常用于资源管理类,例如:线程池、连接池、常量池。
设计规范:1. 构造方法私有化避免手动创建对象。 2. 提供用于获取对象的静态方法或提供一种途径。
- 饿汉式单例模式。
class Singleton1{ // 存储一个实例 private static final Singleton1 instance = new Singleton1(); // 构造方法私有化 private Singleton1(){} // 提供获取对象的方法 public static Singleton1 getInstance(){ return instance; } }
- 懒汉式单例模式。
两种实现方式的特点:class Singleton2{ // 存储一个实例 private static Singleton2 instance; // 构造方法私有化 private Singleton2(){} // 提供获取对象的方法 public synchronized static Singleton2 getInstance(){ if(instance==null){ instance = new Singleton2(); // 创建单例对象 } return instance; } }
1. 饿汉式以空间换时间,可能会浪费空间但节省了创建对象的时间。 2. 懒汉式以时间换空间,会因为创建对象而浪费时间但是节省了空间。
- 懒汉式变种:支持并发,效率高。
演示的代码如下:class Singleton3{ // 静态内部类,加载外部类时不会加载内部类 private static class SingletonInstance{ private static final Singleton3 instance = new Singleton3(); } private Singleton3(){} // 构造方法私有化 // 提供获取对象的方法 public static Singleton3 getInstance(){ return SingletonInstance.instance; // 返回内部类的属性 } }
package com.txw.test;
public class TestSingleton1 {
public static void main(String[] args) {
// Singleton1 sl1 = Singleton1.getInstance();
// Singleton1 sl2 = Singleton1.getInstance();
Singleton2 sl1 = Singleton2.getInstance();
Singleton2 sl2 = Singleton2.getInstance();
System.out.println(sl1);
System.out.println(sl2);
}
}
class Singleton1{
// 存储一个实例
private static final Singleton1 instance = new Singleton1();
// 构造方法私有化
private Singleton1(){}
// 提供获取对象的方法
public static Singleton1 getInstance(){
return instance;
}
}
class Singleton2{
// 存储一个实例
private static Singleton2 instance;
// 构造方法私有化
private Singleton2(){}
// 提供获取对象的方法
public synchronized static Singleton2 getInstance(){
if(instance==null){
instance = new Singleton2(); // 创建单例对象
}
return instance;
}
}
class Singleton3{
private static class SingletonInstance{
private static final Singleton3 instance = new Singleton3();
}
private Singleton3(){} // 构造方法私有化
// 提供获取对象的方法
public static Singleton3 getInstance(){
return SingletonInstance.instance; // 返回内部类的属性
}
}
4 工厂模式
- 概念:将创建对象的代码封装到一个类中,统一分配对象管理对象,便于扩展与维护。
实现代码:
package com.txw.test;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class TestFactory {
public static void main(String[] args) {
List list1 =Factory.getBean(List.class); // Class<List>
Set set = Factory.getBean(Set.class); // Class<Set>
Map map =Factory.getBean(Map.class);
MyInterface mi = Factory.getBean(MyInterface.class);
System.out.println(list1.getClass());
System.out.println(set.getClass());
System.out.println(map.getClass());
System.out.println(mi);
}
}
interface MyInterface{ }
class MyInterfaceImpl implements MyInterface{}
// 工厂模式2.5
class Factory{
private static Properties p = new Properties(); // 存储配置文件信息
static{
InputStream is = Factory.class.getResourceAsStream("./factory.properties");
// 加载配置文件中的内容
try{p.load(is);}catch(Exception e){e.printStackTrace();}
}
public static<T> T getBean(Class<T> cls){//Class<Set> cls
// 读取配置文件
// InputStream is = Factory.class.getResourceAsStream("./factory.properties");
// InputStream is = null;
T result = null;
try {
// is = new FileInputStream("./test/src/com/txw/test/factory.properties");
// 根据key获取value(全类名)
// 获取类名
String clasName = cls.getSimpleName();
String value = p.getProperty(clasName); // java.util.List
Class c = Class.forName(value);
result = (T)c.newInstance(); // 创建对象
}catch(Exception e){
e.printStackTrace();
}
return result;
}
}
创建factory.properties的代码如下:
List=java.util.LinkedList
Set=java.util.LinkedHashSet
Map=java.util.LinkedHashMap
MyInterface=com.txw.test.MyInterfaceImpl
如图所示: