1、什么是反射
反射:reflection
类信息 ——> 对象
对象 ——> 类信息
Java反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象方法地功能称为反射。
2、Class类
Class类是一切的反射根源
Class类表示什么?
很多的人——可以定义一个Person类(有年龄, 性别, 姓名等)
很多的车——可以定义一个Car类(有发动机, 颜色, 车轮等)
很多的类——Class类(类名, 构造方法, 属性, 方法)
得到Class类的对象有三种方式:
第一种形式: Object类中的getClass()方法
第二种形式: 类.class
第三种形式: 通过Class类的forName方法
public class Dog {public int type;private String name;private int age;private String color;private void set(){System.out.println("set");}protected void get(){System.out.println("get");}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;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +", color='" + color + '\'' +'}';}public Dog() {}public Dog(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}}
public class ReflectionDemo {/*** 获取Class对象的三种形式*/@Testpublic void test1(){//通过对象的getClass()方法Dog dog = new Dog("wangwang",4,"白色");Class aClass = dog.getClass(); //字节码//通过类.classClass dogClass = Dog.class;//通过Class.forName方法try {Class aClass1 = Class.forName("com.vince.Dog");} catch (ClassNotFoundException e) {e.printStackTrace();}}}
使用Class类进行对象的实例化操作
调用无参构造进行实例化:
public T newInstance() throws InstantiationException,IllegalAccessException
调用有参构造进行实例化:
public Constructor<?>[] getConstructors() throws SecurityException
获取类、方法、属性
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import org.junit.Test;import java.lang.reflect.*;public class ReflectionDemo {//获取所有的属性@Testpublic void test4(){Class<Dog> dogClass = Dog.class;//获取非私有属性Field[] fields = dogClass.getFields();//System.out.println(fields.length);//获取所有属性(包括私有属性)Field[] declaredFields = dogClass.getDeclaredFields();//System.out.println(declaredFields.length);for (int i = 0; i < declaredFields.length; i++) {int modifiers = declaredFields[i].getModifiers();System.out.println(Modifier.toString(modifiers)+ " "+declaredFields[i].getType()+" "+declaredFields[i].getName());}}//获取所有构造方法@Testpublic void test3(){Class<Dog> dogClass = Dog.class;Constructor<?>[] constructors = dogClass.getConstructors();for (int i = 0; i < constructors.length; i++) {System.out.println(constructors[i].getName());System.out.println(constructors[i].getParameterCount());}try {//获取一个指定的构造方法Constructor<Dog> constructor = dogClass.getConstructor(String.class, int.class, String.class);//调用带参数的构造器来实例化对象Dog dog = constructor.newInstance("小白", 5, "白色");} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}/*** 通过反射来实例化对象*/public void test2(){Class<Dog> dogClass = Dog.class;try {//通过Class对象实例化类对象,调用了默认无参的构造方法Dog dog = (Dog) dogClass.newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}/*** 获取Class对象的三种形式*/@Testpublic void test1(){//通过对象的getClass()方法Dog dog = new Dog("wangwang",4,"白色");Class aClass = dog.getClass(); //字节码//通过类.classClass dogClass = Dog.class;//通过Class.forName方法try {Class aClass1 = Class.forName("com.vince.Dog");} catch (ClassNotFoundException e) {e.printStackTrace();}}}
3、通过Class类取得类信息
取得类所在的包
public Package getPackage() //得到一个类所在的包
public String getName() //得到名字
取得一个类中的全部方法
public Method[] getMethods()
public int getModifiers() //Modifier.toString(mod); // 还原修饰符
public Class<?> getReturnType()
public Class<?>[] getParameterTypes()
public Class<?>[] getExceptionTypes()
public static String toString(int mod)
取得一个类中的全部属性
public Field[] getFields()
public Field[] getDeclaredFields()
public Class<?> getType()
public int getModifiers()
public String getName()
4、通过Class类调用属性或方法
调用类中的方法
调用类中的方法, 传入实例化对象, 以及具体的参数内容
public Object invoke(Object obj,Object… args)
直接调用属性
取得属性
public Object get(Object obj)
//设置属性, 等同于使用“ =” 完成操作
public void set(Object obj,Object value)
//让属性对外部可见
public void setAccessible(boolean flag)
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;import org.junit.Test;import java.lang.reflect.*;public class ReflectionDemo {@Testpublic void test5(){Dog dog = new Dog("wangwang",4,"白色");Class<Dog> dogClass = Dog.class;//获取类的包名Package aPackage = dogClass.getPackage();//System.out.println(aPackage.getName());//获取公共的方法,包括继承的公有方法Method[] methods = dogClass.getMethods();for (int i = 0; i < methods.length; i++) {System.out.println(methods[i]);if (methods[i].getName().equals("toString")){try {String s = (String)methods[i].invoke(dog);System.out.println(s);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}System.out.println("---------------");//访问私有方法,获取到本类中定义的所有方法(不包括父类)Method[] declaredMethods = dogClass.getDeclaredMethods();for (int i = 0; i < declaredMethods.length; i++) {System.out.println(declaredMethods[i]);if (declaredMethods[i].getName().equals("set")){//设置 私有方法可以被访问(去除访问修饰符的检查)declaredMethods[i].setAccessible(true);try {declaredMethods[i].invoke(dog);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}}}
5、动态代理
所谓动态代理, 即通过代理类: Proxy的代理, 接口和实现类之间可以不直接发生联系, 而可以在运行期(Runtime) 实现动态关联。
java动态代理主要是使用java.lang.reflect包中的两个类。
InvocationHandler类
public Object invoke(Object obj,Method method,Object[] obs)
其中第一个参数 obj 指的是代理类, method是被代理的方法, obs是指被代理的方法的参数组。 此方法由代理类来实现。
Proxy类
protected Proxy(InvocationHandler h);
static Class getProxyClass(ClassLoader loader,Class[] interfaces);
static Object newProxyInstance(ClassLoader loader,Class[]interfaces,InvocationHandlerh);
动态代理其实是在运行时生成class, 所以, 我们必须提供一组interface, 然后告诉他class已经实现了这些interface, 而且在生成Proxy的时候, 必须给他提供一个handler, 让他来接管实际的工作。
Subject
package com.vince.proxy;public interface Subject {public void shopping();}
Hotel
package com.vince.proxy;public interface Hotel {public void reserve();}
Person
package com.vince.proxy;public class Person implements Subject,Hotel{@Overridepublic void shopping() {System.out.println("付款,买到心仪的衣服");}@Overridepublic void reserve() {System.out.println("付预定款,打飞的前往目的地");}}
CreatProxy
package com.vince.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/*** 用于动态生成一个代理对象*/public class CreateProxy implements InvocationHandler {private Object target;//被代理的对象//用于创建代理对象的方法public Object create(Object target){this.target = target;Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);return proxy;}/*** 代理对象要执行的方法* @param proxy 代理类对象* @param method 被代理对象的方法* @param args 被代理对象方法的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("上海外寻找客户需要的产品...");System.out.println("跟客户确认产品");method.invoke(target,args);System.out.println("完成本次海淘");return null;}}
TestDemo
package com.vince.proxy;import org.junit.Test;public class TestDemo {@Testpublic void testProxy(){CreateProxy cp = new CreateProxy();//用来创建代理对象的对象Hotel person = new Person();Hotel proxy = (Hotel) cp.create(person);//proxy.shopping();//调用invokeproxy.reserve();}}
6、类加载器原理分析与实现
1、 类的加载过程
JVM将类加载过程分为三个步骤: 装载(Load) , 链接(Link) 和初始化(Initialize)链接又分为三个步骤, 如下图所示:

1)装载: 查找并加载类的二进制数据;
2)链接:
- 验证: 确保被加载类的正确性;
- 准备: 为类的静态变量分配内存, 并将其初始化为默认值;
- 解析: 把类中的符号引用转换为直接引用;
3)初始化: 为类的静态变量赋予正确的初始值;
2、 类的初始化, 类什么时候才被初始化:
- 创建类的实例, 也就是new一个对象
- 访问某个类或接口的静态变量, 或者对该静态变量赋值
- 调用类的静态方法
- 反射(Class.forName(“com.vince.Dog”))
- 初始化一个类的子类(会首先初始化子类的父类)
- JVM启动时标明的启动类, 即文件名和类名相同的那个类
3、 类的加载:
指的是将类的.class文件中的二进制数据读入到内存中, 将其放在运行时数据区的方法区内, 然后在堆区创建一个这个类的Java.lang.Class对象, 用来封装类在方法区类的对象
7、JavaBean
1、JavaBean概念
Bean理解为组件意思, JavaBean就是Java组件, 在广泛的理解就是一个类, 对于组件来说, 关键在于要具有“ 能够被IDE构建工具侦测其属性和事件” 的能力, 通常在Java中 。
2、JavaBean命名规则
- 对于一个名称为xxx的属性, 通常你要写两个方法: getXxx()和setXxx()。 任何浏览这些方法的工具, 都会把get或set后面的第一个字母自动转换为小写。
- 对于布尔型属性, 可以使用以上get和set的方式, 不过也可以把get替换成is。
- Bean的普通方法不必遵循以上的命名规则, 不过它们必须是public的。
- 对于事件, 要使用Swing中处理监听器的方式。 如addWindowListener、removeWindowListener
BeanUtils工具类: http://apache.org/
EMP
package com.vince.bean;/***/public class Emp {private String name;private int age;private int salary;public String getInfo(){return "name="+name+",sage="+age+",salray="+salary;}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;}public int getSalary() {return salary;}public void setSalary(int salary) {this.salary = salary;}}
BeanTest
package com.vince.bean;import org.apache.commons.beanutils.BeanUtils;import org.junit.Test;import java.lang.reflect.InvocationTargetException;/***/public class BeanTest {@Testpublic void test(){//从客户端获取到的数据是这样的String name = "bin";String age = "18";String salary = "20000";Emp emp = new Emp();try {BeanUtils.setProperty(emp,"name",name);BeanUtils.setProperty(emp,"age",age); //实现强转BeanUtils.setProperty(emp,"salary",salary);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}System.out.println(emp.getInfo());}}

8、内省基本概念
内省(Introspector)是Java 语言对 Bean 类属性、 事件的一种缺省处理方法。 例如类 A 中有属性name, 那我们可以通过getName,setName 来得到其值或者设置新的值。
通过 getName/setName 来访问 name 属性, 这就是默认的规则
Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法, 通过这些 API 可以使你不需要了解这个规则, 这些 API 存放于包 java.beans 中, 一般的做法是通过类 Introspector 的getBeanInfo方法 来获取某个对象的 BeanInfo 信息, 然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor), 通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
1、 Introspector类:
Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、 事件和方法的知识提供了一个标准方法。
static BeanInfo getBeanInfo(Class<?> beanClass)在 Java Bean 上进行内省, 了解其所有属性、 公开的方法和事件。
2、 BeanInfo类:
该类实现此 BeanInfo 接口并提供有关其 bean 的方法、 属性、 事件等显式信息。
MethodDescriptor[] getMethodDescriptors()
获得 beans MethodDescriptor。
PropertyDescriptor[] getPropertyDescriptors()
获得 beans PropertyDescriptor。
Properties 属性文件工具类的使用
3、 PropertyDescriptor 类:
PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。
Method getReadMethod()
获得应该用于读取属性值的方法。
Method getWriteMethod()
获得应该用于写入属性值的方法。
4、 MethodDescriptor 类:
MethodDescriptor 描述了一种特殊方法,
即 Java Bean 支持从其他组件对其进行外部访问。
Method getMethod()
获得此 MethodDescriptor 封装的方法。
config.properties
bean.name=com.vince.introspector.Configbean.username=adminbean.password=123bean.url=http://www.163.com
Config
package com.vince.introspector;/***/public class Config {private String username;private String password;private String url;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public Config(String username, String password, String url) {this.username = username;this.password = password;this.url = url;}public Config() {}@Overridepublic String toString() {return "Config{" +"username='" + username + '\'' +", password='" + password + '\'' +", url='" + url + '\'' +'}';}}
BeanFactory
package com.vince.introspector;import com.sun.corba.se.impl.ior.WireObjectKeyTemplate;import java.beans.BeanInfo;import java.beans.IntrospectionException;import java.beans.Introspector;import java.beans.PropertyDescriptor;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Properties;/*** 通过内省的API来装配一个Bean对象,Bean对象的值是通过配置文件中来获取* 目的是为了提高维护性*/public class BeanFactory {private static Properties prop = new Properties();//使用静态代码块读取配置文件static {InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/vince/introspector/config.properties");try {prop.load(in);} catch (IOException e) {e.printStackTrace();}}/*** 获取一个Bean* @param name* @return*/public static Object getBean(String name){Object bean = null;String beanName = prop.getProperty(name);try {Class<?> aClass = Class.forName(beanName);bean = aClass.newInstance();//通过类信息获取javaBean的描述信息BeanInfo beanInfo = Introspector.getBeanInfo(aClass);//通过javaBean描述信息,获取该类的所有属性描述器PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (int i = 0; i < propertyDescriptors.length; i++) {String propertyName = propertyDescriptors[i].getName();Method writeMethod = propertyDescriptors[i].getWriteMethod();if("username".equals(propertyName)) {//调用属性的set方法writeMethod.invoke(bean,prop.getProperty("bean.username"));}else if ("password".equals(propertyName)){writeMethod.invoke(bean,prop.getProperty("bean.password"));}else if("url".equals(propertyName)){writeMethod.invoke(bean,prop.getProperty("bean.url"));}}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IntrospectionException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return bean;}}
BeanTest
package com.vince.introspector;import org.junit.Test;/***/public class BeanTest {@Testpublic void getBeanTest(){Config bean = (Config)BeanFactory.getBean("bean.name");System.out.println(bean);}}
9、初探:理解可配置的AOP框架
补充知识:
- AOP的概念: Aspect Oriented Programming(面向切面编程)
- 可配置 AOP框架实现
AOP使用场景
AOP用来封装横切关注点, 具体可以在下面的场景中使用:
- 权限
- 缓存
- 错误处理
- 调试
- 记录跟踪
- 持久化
- 同步
- 事务
- 。。 。
处理非主流业务,将主流业务与非主流业务完全分开

IManager
package com.vince.aop;public interface IManager {public void add(String item);}
IManagerImpl
package com.vince.aop;import java.util.ArrayList;public class IManagerImpl implements IManager {private ArrayList<String> list = new ArrayList<>();@Overridepublic void add(String item) {// System.out.println("add start "+ System.currentTimeMillis());list.add(item);System.out.println(item);// System.out.println("add end "+ System.currentTimeMillis());}}
Advice
package com.vince.aop;/*** 通知*/public interface Advice {public void beforeAdvice();public void afterAdvice();}
LogAdvice
package com.vince.aop;/*** 切面的实现类*/public class LogAdvice implements Advice{@Overridepublic void beforeAdvice() {System.out.println("start time:"+System.currentTimeMillis());}@Overridepublic void afterAdvice() {System.out.println("end time:"+System.currentTimeMillis());}}
bean.properties
bean.target=com.vince.aop.IManagerImplbean.advice=com.vince.aop.LogAdvicebean=com.vince.aop.ProxyFactoryBean
ProxyFactoryBean
package com.vince.aop;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyFactoryBean implements InvocationHandler {private Object target;private Advice advice;public Object getProxy(){Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);return proxy;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {advice.beforeAdvice();Object obj = method.invoke(target, args);advice.afterAdvice();return obj;}public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Advice getAdvice() {return advice;}public void setAdvice(Advice advice) {this.advice = advice;}}
BeanFactory
package com.vince.aop;import java.beans.BeanInfo;import java.beans.IntrospectionException;import java.beans.Introspector;import java.beans.PropertyDescriptor;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Properties;/*** AOP框架的简单实现*/public class BeanFactory {Properties prop = new Properties();public BeanFactory(InputStream in){try {prop.load(in);} catch (IOException e) {e.printStackTrace();}}/*** 获取一个bean实例* @param name* @return*/public Object getBean(String name){String className = prop.getProperty(name);Object bean = null;try {//获取ProxyFactoryBean的class对象Class<?> aClass = Class.forName(className);bean = aClass.newInstance();//实例化对象//根据配置文件实例化target和advice对象Object target = Class.forName(prop.getProperty(name + ".target")).newInstance();Object advice = Class.forName(prop.getProperty(name + ".advice")).newInstance();//通过内省实现对 ProxyFactoryBean的属性赋值BeanInfo beanInfo = Introspector.getBeanInfo(aClass);PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for(PropertyDescriptor pd: propertyDescriptors){String propertyName = pd.getName();Method writeMethod = pd.getWriteMethod();if("target".equals(propertyName)){writeMethod.invoke(bean,target);}else if("advice".equals(propertyName)){writeMethod.invoke(bean,advice);}}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IntrospectionException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return bean;}}
AOPTest
package com.vince.aop;import org.junit.Test;import java.io.InputStream;public class AOPTest {@Testpublic void test(){//1读取配置 文件InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/vince/aop/bean.properties");//2创建Bean的工厂对象BeanFactory beanFactory = new BeanFactory(in);//3获取代理对象ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) beanFactory.getBean("bean");IManager proxy = (IManager) proxyFactoryBean.getProxy();proxy.add("我是一只猫");}}
10、单例模式优化
- 使用同步保正线程安全 synchronized
- 使用volatile关键字
volatile提醒编译器它后面所定义的变量随时都有可能改变, 因此编译后的程序每次需要存储或读取这个变量的时候, 都会直接从变量地址中读取数据。 如果没有volatile关键字, 则编译器可能优化读取和存储, 可能暂时使用寄存器中的值, 如果这个变量由别的程序更新了的话,将出现不一致的现象。 - 防止反射调用私有构造方法
- 让单例类序例化安全
package com.vince.singleton;import java.io.Serializable;/*** 单例模式* 1、多线程访问的安全问题* 2、加上volatile关键字保证变量的一致性* 3、防止反射调用私有构造方法* 4、让单例类可以被序列化*/public class Singleton implements Serializable {private volatile static Singleton singleton = null;private Singleton(){if (singleton!=null){throw new RuntimeException("此类对象为单例模式,已经被实例化了");}}public static Singleton getInstance() {if (singleton==null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}}
