1.反射
反射就是将java中的各成分映射成相应的Java类
如何得到各个字节码对应的实例对象( Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date"); 反射中用的最多,因为可以根据字符串,直接返回该类的字节码。
九个预定义Class实例对象:
8个基本数据类型:即 boolean、byte、char、short、int、long、float 、double和void
参看Class.isPrimitive方法的帮助
Int.class == Integer.class 返回的false
Int.class == Integer.TYPE 返回是true èInteger.TYPE是返回该对应的基本数据类型的字节码
数组类型的Class实例对象
Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…
Constructor是操作类中构造函数的
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
Field是操作类中成员变量的
Field类代表某个类中的一个成员变量
演示用eclipse自动生成Java类的构造方法
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。
示例代码:
ReflectPoint point = new ReflectPoint(1,7);//x为私有的
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");//<span style="font-family:Calibri;font-size:14px;">NoSuchFieldException</span>
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");//<span style="font-family:Calibri;font-size:14px;">IllegalAccessException</span>
x.setAccessible(true);
System.out.println(x.get(point));
注意:
比较字节码是用 == 而不是equals
此时就会显得你很专业
要加上注释,不然人家就不会知道你很专业
//这里应该用 == ,这里是同一份字节码
将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
代码如下:
private static void changeStringValue(Object obj) throws Exception{
Field[] fields = obj.getClass().getFields();
for(Field field : fields){
//这里应该用 == ,这里是同一份字节码
if(field.getType() == String.class){
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
Method 是操作类中成员方法的
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
用反射方式执行某个类中的main方法
目标: 写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?
因为可以直接用forName通过一个字符串来调用该对应的类
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决方法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"});,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Int.class的getSuperclass的父类为null
Int[].class的getSuperclass的父类是Object
Arrays.asList()方法处理int[]和String[]时的差异。
Jdk 1.5:Arrays.asList(T… t)
Jdl 1.4:Arrays.asList(Object[] obj)
1.5为了兼容1.4,可以传递数据进去
String[]传递进去可以当作Object[]去处理
打印结果为:[a,b,c]
Int[]传递进去的时候却不能转化为Object[]所以1.4处理不了,返回去给1.5进行处理
打印结果为:[ [I (hashcode) ]
int[] a = new int[3];
Object obj = a;
//Object[] obj1 = a //有错!
Array工具类用于完成对数组的反射操作。
private static void printObject(Object obj) {
if(obj.getClass().isArray()){
int len = Array.getLength(obj);
for(int i=0;i<len;i++) {
System.out.println(Array.get(obj, i));
}
} else {
System.out.println(obj);
}
}
思考题:怎么得到数组中的元素类型?
一些面试题
Class.forName(“java.lang.String”);
得到这个类的字节码
1.此类的字节码已经加载到内存中去,不需要去加载了,直接去内存中找到即可
2.虚拟机中还没此类的字节码,就用类加载器去加载,加载以后就把字节码缓存起来,返回此类的字节码
面试题:forName的作用
作用:返回字节码:返回的方式有两种:1.这份字节码曾经被加载过,直接返回;2.Java虚拟机中还没有这份字节码,就用类类加载器去加载,加载完成就缓存起来,以后再用就不需要去加载了。
hashCode的作用
hashCode的作用必须是在hash存储结构中才有用,hashCode可以提高在hash存储结构中的查找效率,比如有1W个对象存储在hash中,我们查找最坏的是要查找1W次,当有了hashCode的时候,我们就可以将这1W个对象根据hashCode分成若干个区域,当要查找某个对象时,先根据hashCode判断出该对象在哪个区域里,再在那个区域中去查找,这样子就可以提高查询效率!所以,为了让相等的对象放在同一个区域,如果两个对象equals相等,那么也必须要让他们的hashCode相等(要复写equals方法和hashCodefanfa),但是如果存储结构不是hash的时候,就没有必要去Override hashCode.
还有一个作用,那就是会引起内存泄漏
内存泄露的另外一种情况:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。
2.JavaBean内省
内省对应的英文单词为IntroSpector,它主要用于对JavaBean进行操作,JavaBean是一种特殊的Java类,其中的某些方法符合某种命名规则,如果一个Java类中的一些方法符合某种命名规则,则可以把它当作JavaBean来使用
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
3.beanutils工具包
用struts的迭代标签不能迭代出枚举元素的属性,而用jstl的迭代标签则可以。采用BeanUtils去获取带有抽象方法的枚举类的成员对象的属性时,会出现错误,要自己用内省加暴力反射方式才可以获取。主要原因是枚举类的抽象子类不是public类型的。
public static void main(String[] args) {
// TODO Auto-generated method stub
/*System.out.println(
PropertyUtils.getProperty(Sex.NONE, "title"));*/
Object bean = Sex.NONE;
BeanInfo beanInfo = null;
try {
beanInfo = Introspector.getBeanInfo(bean.getClass());
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor property:properties)
{
if(property.getName().equals("title"))
{
Method method = property.getReadMethod();
method.setAccessible(true);
Object retVal;
try {
retVal = method.invoke(bean, null);
System.out.println(retVal);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
4.类加载器
类加载器就是把类加载到JVM中去
Java中可以安装多个类加载器,系统默认三个主要的类加载器,每个负责加载特定位置的类:
BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap(C++编写的一段二进制代码,镶嵌在JVM中)
类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
面试题:我们可不可以自己写一个类也叫java.lang.System?
答:一般是不可以的,Java中有委托加载机制,会通过BootStrap加载器加载rt.jar中的System,而不会找到你自己的写的System,但不是不可以的,你可以自己去写一个类加载器去加载自己写的System
自定义类加载器的原理
class MyClassLoader extends ClassLoader {
//实现findClass方法即可
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
}
package cn.itcast.day2;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
public class MyClassLoader extends ClassLoader {
public static void main(String[] args) throws Exception{
String srcPath = "D:\\Users\\pphdsny\\Workspaces\\MyEclipse 8.5\\javaenhance\\bin\\cn\\itcast\\day2\\ClassLoaderAttachment.class";
String destDir = "itcastlib";
System.out.println(srcPath);
FileInputStream fis = new FileInputStream(new File(srcPath));
String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
String destPath = destDir + System.getProperty("file.separator") + destFileName;
FileOutputStream fos = new FileOutputStream(new File(destPath));
cypher(fis, fos);
}
public static void cypher(InputStream ips,OutputStream ops) throws Exception{
int b = -1;
while((b = ips.read()) != -1){
ops.write(b ^ 0xff);
}
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String classFileName = classDir + "\\" + name.substring(name.lastIndexOf('.')+1) + ".class";
System.out.println("aaa");
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis, bos);
fis.close();
byte[] bytes = bos.toByteArray();
return defineClass(bytes, 0, bytes.length);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader() {
// TODO Auto-generated constructor stub
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
}
ClassLoaderAttachment
package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderAttachment extends Date {
@Override
public String toString() {
// TODO Auto-generated method stub
return "hello u!";
}
}
用于测试
package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderTest {
/**
* @param args
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
System.out.println(System.class.getClassLoader());
ClassLoader loader = AnnotationTest.class.getClassLoader();
while(loader != null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
/* System.out.println(loader);
System.out.println(new ClassLoaderAttachment().toString());*/
Class clazz = new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");
Date d = (Date)clazz.newInstance();
System.out.println(d);
System.out.println(d.getClass().getClassLoader().getParent().getClass().getName());
}
}
5.代理
代理架构图
AOP
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
动态代理技术
动态代理的工作原理图
实现AOP功能的封装与配置
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
源代码
AopFrameworkTest主要用于测试
package cn.itcast.day3.aopframework;
import java.io.InputStream;
public class AopFrameworkTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
Object bean = new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
}
}
package cn.itcast.day3.aopframework;
import java.io.InputStream;
import java.util.Properties;
import cn.itcast.day3.Advice;
public class BeanFactory {
Properties props = new Properties();
public BeanFactory(InputStream ips) throws Exception{
props.load(ips);
}
public Object getBean(String name){
String clazzName = props.getProperty(name);
Object bean = null;
try {
Class clazz = Class.forName(clazzName);
bean = clazz.newInstance();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(bean instanceof ProxyFactoryBean){
Object proxy = null;
ProxyFactoryBean proxyBean = (ProxyFactoryBean)bean;
try {
Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();
Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
proxyBean.setAdvice(advice);
proxyBean.setTarget(target);
proxy = proxyBean.getProxy();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
ProxyFactoryBean
package cn.itcast.day3.aopframework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.itcast.day3.Advice;
public class ProxyFactoryBean {
private Object target;
private Advice advice;
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;
}
public Object getProxy() {
// TODO Auto-generated method stub
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
/*new Class[]{Object.class}, */
target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
//ArrayList target = new ArrayList();
/*long beginTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " run time is " + (endTime - beginTime));
return retVal;*/
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy;
}
}
config.properties配置文件
#xxx=java.util.ArrayList
xxx=cn.itcast.day3.aopframework.ProxyFactoryBean
xxx.advice=cn.itcast.day3.MyAdvice
xxx.target=java.util.ArrayList