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;
}
@Override
public 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对象的三种形式
*/
@Test
public void test1(){
//通过对象的getClass()方法
Dog dog = new Dog("wangwang",4,"白色");
Class aClass = dog.getClass(); //字节码
//通过类.class
Class 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 {
//获取所有的属性
@Test
public 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());
}
}
//获取所有构造方法
@Test
public 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对象的三种形式
*/
@Test
public void test1(){
//通过对象的getClass()方法
Dog dog = new Dog("wangwang",4,"白色");
Class aClass = dog.getClass(); //字节码
//通过类.class
Class 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 {
@Test
public 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{
@Override
public void shopping() {
System.out.println("付款,买到心仪的衣服");
}
@Override
public 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
*/
@Override
public 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 {
@Test
public void testProxy(){
CreateProxy cp = new CreateProxy();//用来创建代理对象的对象
Hotel person = new Person();
Hotel proxy = (Hotel) cp.create(person);
//proxy.shopping();//调用invoke
proxy.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 {
@Test
public 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.Config
bean.username=admin
bean.password=123
bean.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() {
}
@Override
public 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 {
@Test
public 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<>();
@Override
public 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{
@Override
public void beforeAdvice() {
System.out.println("start time:"+System.currentTimeMillis());
}
@Override
public void afterAdvice() {
System.out.println("end time:"+System.currentTimeMillis());
}
}
bean.properties
bean.target=com.vince.aop.IManagerImpl
bean.advice=com.vince.aop.LogAdvice
bean=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;
}
@Override
public 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 {
@Test
public 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;
}
}