黑马程序员————java1.5高新技术学习笔记

  •                                                        ----------  android培训java培训、期待与您交流! ----------



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是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。
  setId()的属性名 id
  isLast()的属性名 last
  setCPU的属性名是什么? CPU
  getUPS的属性名是什么? UPS

总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。

一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:
  在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地!
   JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。

3.beanutils工具包

演示用eclipse如何加入jar包,先只是引入beanutils包,等程序运行出错后再引入logging包。
在前面内省例子的基础上,用BeanUtils类先get原来设置好的属性,再将其set为一个新值。
  get属性时返回的结果为字符串,set属性时可以接受任意类型的对象,通常使用字符串。
l用PropertyUtils类先get原来设置好的属性,再将其set为一个新值。
  get属性时返回的结果为该属性本来的类型,set属性时只接受该属性本来的类型。
演示去掉JavaBean(ReflectPoint)的public修饰符时,BeanUtils工具包访问javabean属性时出现的问题。

用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

{                      {                       {

------------------------------------------------------切面

....            ....              ......

------------------------------------------------------切面

}                       }                       }

交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

------------------------------------------------------切面

func1         func2            func3

{             {                {

....            ....              ......

}             }                }

------------------------------------------------------切面

使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
  1.在调用目标方法之前
  2.在调用目标方法之后
  3.在调用目标方法前后
  4.在处理目标方法异常的catch块中
动态代理的工作原理图

 

 实现AOP功能的封装与配置
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
lBeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:

  #xxx=java.util.ArrayList

  xxx=cn.itcast.ProxyFactoryBean

  xxx.target=java.util.ArrayList

  xxx.advice=cn.itcast.MyAdvice

ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
  目标
  通知
编写客户端应用:
  编写实现Advice接口的类和在配置文件中进行配置
  调用BeanFactory获取对象
源代码

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值