黑马程序员 Java自学总结十九 Java高新技术第二天
2013-08-09 15:26
645 查看
------ ASP.Net+Android+IO开发.Net培训期待与您交流! ------
总结内容来源于张孝祥老师的Java高新技术
内省IntroSpector→JavaBean
内省(Introspector)是java语言对Bean类属性、事件的一种缺省处理方法。
JavaBean主要用于传递数据信息,其方法用于访问私有变量,且方法名符合某种规则。
例如 : 类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name 属性,这就是JavaBean默认的规则。
内省主要是对JavaBean进行操作。JavaBean内部的方法要按照某种规则命名,JavaBean可以作为普通类进行操作;普通类如果内部有set()、get()方法,也可以当做JavaBean使用。
JavaBean的属性是通过get()和set()方法推断出来的,即去掉get、set后的字母,例如,一个类有方法getAge( )和setAge( ) , 那么bean的属性为age,而不是成员变量,因为成员变量是private看不见的。
获得属性名的规则:如果属性名的第二个字母是小写,则把第一个字母小写。例如,gettime—>time,setTime—>time,getCPU—>CPU。
PropertyDescriptor类
PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:
1、getPropertyType( ),获得属性的Class对象。
2、getReadMethod( ),获得用于读取属性值的方法;getWriteMethod( ),获得用于写入属性值的方法。
3、hashCode( ),获取对象的哈希值。
4、setReadMethod( ),设置用于读取属性值的方法;setWriteMethod( ),设置用于写入属性值的方法;
通过属性名获取对应的值,利用反射方法 ;
ElementBean e = new ElementBean(3,7);
String pn = "x";
PropertyDescriptor pd = new PropertyDescriptor(pn, e.getClass());
Method methodGet = pd.getReadMethod();//getReadMethod()对应get方法
Object retValX = methodGet.invoke(e);
System.out.println(retVal)
给某个属性设置值 :
ElementBean e = new ElementBean(3,7);
String propertyName = "x";
PropertyDescriptor pd = new PropertyDescriptor(pn,e.getClass());
Method methodSetX = pd.getWriteMethod();//getWriteMethod()对应set方法
Object value = 7;
methodSetX.invoke(e, value);
eclipse快速创建get()和set()方法:
右键→Source→Generate Geters and Setters,创建get()和set()方法。
eclipse把一些代码生成方法,提高复用性
选择一些代码,右键→Refactor→Extract Method
Introspector类
将JavaBean中的属性封装起来进行操作。在程序把一个类当做JavaBean来看,就是调用Introspector.getBeanInfo() 方法,得到的BeanInfo对象封装了把这个类当做JavaBean看的结果信息,即属性的信息。需要导包java.beans.*。
getPropertyDescriptors(),获得属性的描述,可以采用遍历BeanInfo的方法,来查找、设置类的属性。
代码
[java] view
plaincopy
private static Object getProperties_2(ElementBean e, String propertyName)throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
BeanInfo beanInfo = Introspector. getBeanInfo(e.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor p : pds){
if(p.getName().equals(propertyName)){
Method methodGet = p.getReadMethod();
retVal = methodGet.invoke(e);
}
}
return retVal;
}
通过这PropertyDescriptor类和Introspector类的比较可以看出,都是需要获得PropertyDescriptor,只是方式不一样:前者通过创建对象直接获得,后者需要遍历,所以使用PropertyDescriptor类更加方便。
BeanUtils工具包 , 为JavaBean提供更多、放方便的功能 .
使用时需要配合acpche提供的日志包logging一起使用 .
可以通过BuildPath,添加额外的jar包,或者工程下建立lib目录,将jar包复制进来,再加载这个jar包:右键→add to BuildPath。使用时需要导包:org.apache.commons.beanutils.BeanUtils。
下载的BeanUtils
beanutils.jar = beanutils-core.jar + beanutils-bean-collections.jar
获得属性的值,例如,BeanUtils.getProperty(pt1,"x"),返回字符串
设置属性的值,例如,BeanUtils.setProperty(pt1,"y",22)
将一个对象的属性值复制到另一个对象的属性 , copyProperties(Object dest, Object orig),需要保证属性一致。
BeanUtils的特点:
1、对基本数据类型的属性的操作:在WEB开发、使用中,录入和显示时,值会被转换成字符串,但底层运算用的是基本类型,这些类型转到动作由BeanUtils自动完成。
2、对引用数据类型的属性的操作:
首先在类中必须有对象,不能是null,
例如 : private Date birthday=new Date();。
操作的是对象的属性而不是整个对象
例如 : BeanUtils.setProperty(pt1,"birthday.time",121);
Java7的新特性:
1、Map和JavaBean之间可以进行相互转换,key是属性,value是属性的值。
方法describe( bean ):把JavaBean转换成Map;
方法populate( bean , map ):把Map封装成JavaBean ;
2、Map的简化书写形式 , 如下
Map map = (name:Kim,age:18);
BeanUtilssetProperty( )方法可以把Map看作成Bean ,
例如 : BeanUtils.setProperty(map,"name","Kim");
PropertyUtils类
和BeanUtils不同在于,运行getProperty、setProperty操作时,没有类型转换,使用属性的原有类型或者包装类。
全篇代码 :
[java] view
plaincopy
package com.itheima;
import java.beans.*;
import java.lang.reflect.*;
import java.util.Map;
import org.apache.commons.beanutils.*;
public class IntroSpectorTest {
@SuppressWarnings({ "unused", "rawtypes" })
public static void main(String[] args) throws Exception{
ElementBean e = new ElementBean(3,7);
String propertyName = "x";
Object value = 7;
//使用自己写的方法获取"x"的值
System. out.println( getProperties(e, propertyName));
System. out.println( getProperties_2(e, propertyName));
//使用BeanUtils设置"x"并获取其值
BeanUtils. setProperty(e, "x", "9");
System. out.println(BeanUtils. getProperty(e, propertyName));
//使用BeanUtils设置并获取属性birthday的值
BeanUtils. setProperty(e, "birthday.time", 1111);
System. out.println(BeanUtils. getProperty(e, "birthday.time"));
Map map = BeanUtils. describe(e);
System. out.println(map);
}
public static void setProperties(ElementBean e, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName,e.getClass());
Method methodSetX = pd.getWriteMethod();
methodSetX.invoke(e, value);
}
public static Object getProperties(ElementBean e, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, e.getClass());
Method methodGet = pd.getReadMethod();
Object retValX = methodGet.invoke(e);
return retValX;
}
public static Object getProperties_2(ElementBean e, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
BeanInfo beanInfo = Introspector. getBeanInfo(e.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor p : pds){
if(p.getName().equals(propertyName)){
Method methodGet = p.getReadMethod();
retVal = methodGet.invoke(e);
}
}
return retVal;
}
}
注解Annotation
JDK1.5出现的新特性。在java.lang.annotation包中。
对于过时的语句,java会提示过时了,通过@SuppressWarnings("Deprecation")在DOS中取消提示,但Eclipse无 法取消。这就是注解 .
一个注解就是一个类。
@SuppressWarnings,取消警告。
@Deprecated, 这个方法或类不再建议使用 , 在新版本中有其他方法或类可以代替这个使用,以后的版本也不会再更新这个方法或类 .
HashSet集合中,对象必须覆盖Object类的equals()方法,否则会继续使用Object类的equals()方法进行比较,错误的比较方 法。覆盖equals()方法,参数必须一致,为了防止错误写入本类的对象,加入-------->
@Override,必须正确覆盖父类方法,不是创建新方法。
总结 : 注解相当于一种标记。编译器、开发工具、javac通过反射获得注解里的内容,进而明确应该做什么、不应该做什么。注解可以加在包、类、属 性、方法、参数及局部变量之上。
注解的应用
在源程序中,调用一个类,如果这个类会用到注解,就需要先准备好注解类,然后类再调用注解类的对象 . 注意 : 类上的注解都是调用的注解的对象 , 而非注解类 . 可以加多个注解 .
注解类的写法类似接口,@interface。先写好注解类A, 将注解放在类B中,类C在调用类B时通过反射获得注解类A,进而明确需要做什么。
main()方法必须放在一个类下,但与这个类不一定有所属关系。
在注解类A上加注解B,这个注解B只为这个注解类A服务,B称为“元注解”。类似的还有元信息、元数据。
元注解有2个:Rentention和Target。对注解类的注解,可以理解为注解类的属性。
Rentention元注解
注解的生命周期:Java源文件—》class文件—》内存中的字节码。编译或者运行时,都有可能会取消注解。Rentention的3种取值意味让注解 保留到哪个阶段,RententionPolicy.SOURCE、RententionPolicy.CLASS(默认值)、 RententionPolicy.RUNTIME。
@Override、@SuppressWarnings是默认保留到SOURCE阶段;@Deprecated是保留到RUNTIME阶段。
Rentention相当于注解类的一个属性,因为Rentention的值不同,注解类保留到的阶段不同。注解类内部Rentention的值使用value表示,例如,@Deprecated中,value=Runtime。
Rentention的值是枚举RententionPolicy的值,只有3个:SOURCE、CLASS、RUNTIME。
Target元注解
性质和Rentention一样,都是注解类的属性,表示注解类应该在什么位置,对那一块的数据有效。例如,@Target(ElementType.METHOD)//表示在方法上有效
Target内部的值使用枚举ElementType表示,表示的主要位置有:注解、构造方法、属性、局部变量、函数、包、参数和类(默认值)。表示多个位置有效可以使用数组,例如,@Target({ElementType.METHOD,ElementType.TYPE})。
类、接口、枚举、注解这一类事物用TYPE表示,Class的父类,JDK1.5的新特性。
注解Annotation的属性
注解的基本属性
属性可以给注解提供更加详细的信息。
注解相当于接口,属性相当于接口的方法。例如,@ItcastAnnotation(color="black"),注解ItcastAnnotation中必须有String color();语句 , 给属性赋值,取值时类似调用方法例如,System.out.println(annotation.color()) ;
如果只有value属性,没有其他属性,可以不写=,只针对value,例如,@SuppressWarnings("Deprecation")。或者 有其他属性而且有缺省值,例如,String color() default "blue";,此时value单独出现,不用value=。
注意 : 引用注解时,所有的属性必须全部出现,除非属性有缺省值。
获得注解的属性的值,例如
if(AnnotationDemo.class.isAnnotationPresent(ItcastAnnotation.class)){
ItcastAnnotation annotation =(ItcastAnnotation)AnnotationDemo.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.color());
}
}
利用反射获得注解的对象,在让该对象调用属性对应的方法。注意类型转换。
Rentention和Target也是属性,都是value对应的值,值的类型分别是RententionPolicy和ElementType,例如,@Retention(value=RetentionPolicy.RUNTIME)。
注解的高级属性
给注解增加高级属性,数组、枚举、注解。
数组类型的属性
例如,int[] arr() default {3,7,5};,MyAnnotation(arr={3,7,6})。如果数组只有1个元素,可以不加{}。 @Target({ElementType.METHOD,ElementType.TYPE})也是数组类型的属性。
枚举类型的属性
//注解类内部的内容
EnumerationTest2.TrafficLamp lamp() default EnumerationDemo.TrafficLamp.RED;
//调用注解的类上
@ItcastAnnotation(lamp=EnumerationDemo.TrafficLamp.YELLOW)
//对注解进行操作
System.out.println(annotation.lamp().nextLamp());
注解类型的属性
将一个注解类作为属性加入到另一个注解类中。
MetaAnnotation annotationAtt() default @MetaAnnotation("Jobs")
@ItcastAnnotation(annotationAtt=@MetaAnnotation("lmn"))
annotation.annotationAtt().value()
注解的返回值可以是8个基本类型、String、Class、枚举以及前面类型的数组,内部还有属性。
需要详细学习注解,可以通过java语言规范,即languagespecification。
注解类的代码:
[java] view
plaincopy
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解的注解又叫元注解
@Retention(RetentionPolicy.RUNTIME )//决定注解的生命周期
@Target({ElementType. TYPE,ElementType. METHOD}) //决定注解能放在什么位置
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {1,2,3};
EnumTest2.Trafficlamp lamp() default EnumTest2.Trafficlamp. RED;
MetaAnnotation annotationAttr() default @MetaAnnotation( "xyz");
Class classAttr();
}
调用注解类的代码:
package com.itheima;
@ItcastAnnotation(value="abc" ,annotationAttr=@MetaAnnotation ("lmn" ),classAttr=String.class)
public class AnnotationTest {
@ItcastAnnotation(value= "abc",classAttr=String. class)
public static void main(String[] args) {
System. runFinalizersOnExit( true);
Class classAT = AnnotationTest.class;
Class classIA = ItcastAnnotation.class;
if( classAT.isAnnotationPresent(classIA)){
ItcastAnnotation ia = (ItcastAnnotation)classAT.getAnnotation(classIA);
System. out.println(ia.color());
System. out.println(ia.value());
System. out.println(ia.arrayAttr(). length);
System. out.println(ia.lamp().nextTra());
System. out.println(ia.annotationAttr().value());
System. out.println(ia.classAttr());
}
}
@Deprecated//已过时,这个方法或类不再建议使用
public void sayHello(){
System. out.println( "hi,传智播客");
}
}
泛型 Generic
一.问题的引入:为什么要用泛型?
引入泛型的原因之一:在使用集合时,可以向定义好的某一集合中存入任意类型的数据,而我们希望整个集合中的类型都是一样的,当加入的不期望的类型后,只有到运行期才能发现。 而且取出的类型需要强制转换。
[java] view
plaincopy
//没有使用泛型:
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
// int i = (Integer)collection.get(1);//这句会报错</span>
//使用泛型的代码:
// 在集合中的应用
ArrayList<String> collection1 = new ArrayList<String>();
// collection1.add(1);
// collection1.add(1L);
collection1.add("String");
String i = collection1.get(0);// 在反射中的应用
//用反射实现操作:String str = new String(new StringBuffer("abc"));
Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);
String str1 = constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str1.charAt(2));
从上面两段代码的对比中,很容易看出使用泛型的好处:1.限定了集合元素的类型;2.省去了强制转换的过程。
JDK升级一般可分为三个大的方面:1.简化书写;2.提高效率;3.提高安全性。而泛型就属于其中的第三方面安全,它是一种安全机制。
二.利用反射穿透泛型限制
泛型能绝对保证集合中存入数据都是它限定的类型吗?
[java] view
plaincopy
ArrayList<Integer> collection2 = new ArrayList<Integer>();
System.out.println(collection1.getClass()==collection2.getClass());
collection2.add(“123”);//这句会报错
collection2.getClass().getMethod("add", Object.class).invoke(collection2, "hello");
System.out.println(collection2.get(0)); //结果却为hello
三.ArrayList<E>类定义和ArrayList<Integer>类引用中涉及的术语:
1.ArrayList<E>:这个整体称为“泛型类型”,其中的“E”称为 类型变量 或 类型参数 。
2.ArrayList<Integer>:整体称为“参数化的类型”,“Integer”称为“类型参数的实例”或“实际类型参数”,"<>"读为typeof,ArrayList称为“原始类型”。
3.参数化类型与原始类型的兼容性:可以互相引用,但编译器会报告警告。
4.参数化类型不考虑类型参数的继承关系
5.编译器不允许创建类型变量的数组。在创建数组实例时,数组的元素不能使用参数化的类型。
[java] view
plaincopy
Collection<String> c = new Vector();
Collection c1 = new Vector<String>();
// 见第4条
//错误语句 Vector<String> v=new Vector<Object>();
//错误语句 Vector<Object> v1=new Vector<String>();
Vector v2=new Vector<String>();
Vector<Object> v3 = v2;
//错误语句 Vector <Integer> [] vectorList = new Vector <Integer>[10];//见第5条。
Vector <Integer[]> vectorList = new Vector <Integer[]>();
四.扩展应用:泛型中的通配符“?”
1.限定通配符的上边界
Vector<? extends Number> x = new Vector<Integer>();正确的
Vector<? extends Number> x = new Vector<String>():错误的。
2.限定通配符的下边界
Vector<? super Integer> x = new Vector<Number>();正确的。
Vector<? super Integer> x = new Vector<Byte>();错误的。
注意:限定通配符总是包括它自己。
[java] view
plaincopy
printCollection_2(collection2);
// Class<Number> x = String.class.asSubclass(Number.class);//错误。
// Class x1 = String.class.asSubclass(Number.class);
// Class<String> y = Class.forName("java.lang.String");//错误
Class y = Class.forName("java.lang.String");//要抛出异常。
Class<?> y1;
Class<String> y2 = null;
y1=y2;
// y2=y1;
五.泛型集合类的综合应用案例
[java] view
plaincopy
HashMap<String,Integer> hm = new HashMap<String,Integer>();
hm.put("zyq", 18);
hm.put("zsy", 49);
hm.put("zyx", 25);
Set<Map.Entry<String,Integer>> entrySet = hm.entrySet();
for(Map.Entry<String, Integer> entry:entrySet){
System.out.println(entry.getKey() + ":" + entry.getValue());
}
六.定义泛型方法
1.泛型类似C++中的模板,但没有C++中的功能强大。
2.泛型的实际类型参数只能是引用数据类型,例如:对于HashMap<K,V>,K和V不能是八种基本数据类型。
3.异常也可以用泛型
4.类型参数的类型推断
例一:模拟C++中的模板实现求和功能(但没有C++中好用)
[java] view
plaincopy
add(1,2);
Number x1 = add(1,1.4);//int 和float的最大公共集是Number
Object x2 = add(1,"abc");//String 和int的最大公共集是Object
ivate static<T> T add(T x,T y){//取得的T是 x 和 y的最大公共集,其实也是java中多态的体现。
return null;
}
例二:实现数组中的元素位置交换
[java] view
plaincopy
swap(new String[]{"abc","123","hello"},0,1);
// swap(int[]{1,2,3,4},2,3);//int是基本数据类型,所以不能实现。
[java] view plaincopy
private static<T> void swap(T[] t ,int x,int y){
T temp = t[x];
t[x] = t[y];
t[y] = temp;
}
例三:
[java] view
plaincopy
ArrayList<Integer> al = new ArrayList<Integer>();
al.add((Integer)autoConvert(345));
System.out.println(al.get(0));
String s = autoConvert("123");
System.out.println(s);
fillArray(new Integer[]{1,3,4},1);
copy1(new Vector<String>(), new String[]{"123","5674"});
copy2(new Date[20], new String[10]);//这个是两个取交集。
// copy1(new Vector<Date>(), new String[]{"123","5674"});
//类型推断的传播性,Vector中T已经指定为Date,后边的T也应该是Date,否则报错。
public static<T> void fillArray(T[] t,T obj){
for(int x= 0;x<t.length;x++){
t[x]= obj;
}
}
public static <T> T autoConvert(Object obj){
return (T)obj;
}
public static <T>void copy1(Collection<T> des ,T[] src){
}
public static <T>void copy2(T[] des ,T[] src){
}
七、泛型类
[java] view
plaincopy
import java.util.Set;
//dao data access object:CRUD
public class GenericDao<E> {
//增
public void add(E x){
}
//删
public void delete(int id){
}
public void delete(E obj){
}
//查
public E findByID(int id){
return null;
}
public E findByName(String name){
return null;
}
public Set<E> findByConditions(String where){
return null;
}
//改
public void update(E obj){
}
public static<E> void update1(E obj){//静态方法不能用类上定义的泛型参数,但可以自己定义泛型,这里的E和类上的E是两码事。
}
}
GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
dao.add(new ReflectPoint(5, 7));
// String s = dao.findByID(1);//返回的也必须是ReflectPoint
八、通过反射获得泛型的参数化类型
没办法通过泛型的引用,用反射 获取参数化类型的类型参数。
但可以通过反射获得方法的参数列表,从参数列表中获取参数化类型的原始类型和实际类型参数
[java] view
plaincopy
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;
class GenericTest {
public static void main(String[] args) throws Exception{
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types =applyMethod.getGenericParameterTypes();
ParameterizedType pType =(ParameterizedType) types[0];
System.out.println(pType.getClass());
System.out.println(pType.getOwnerType());
System.out.println(pType.getRawType());
System.out.println(pType.getActualTypeArguments()[0]);
}
public static void applyVector(Vector<Date> v){
}
/*
public static void applyVector(Vector<Integer> v){ //这个方法和上边的方法是同一个方法。
}*/
}
类加载器
什么叫类加载器? 加载类的工具.
java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个负责特定位置的类.
BootStrap xitClassLoader AppClassLoader
类加载器也是java类,因为其他是java类的类加载器也要被类加载器加载,显然必须有第一个类加载器不是java类, 这正是:BootStrap,它是由C++编写的.
java虚拟机中类加载器之间的父子关系和管辖范围:
Bootstrap ClassLoader ---->JRE/lib/rt.jar
↑
Extension ClassLoader ---->JRE/lib/ext/.jar
↑
ApplicationClassLoader ---->ClassPath指定的所有jar或目录。
↑
MyClassLoader ---->指定的特殊目录
↑
itcastClassLoader---->指定的特殊目录
[java] view
plaincopy
package study.day2;
import java.util.Date;
public class ClassLoderTest {
public static void main(String[] args) throws Exception {
System.out.println(ClassLoderTest.class.getClassLoader().getClass().getName());
System.out.println(System.class.getClassLoader());
ClassLoader loader = ClassLoader.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
System.out.println(new ClassLoaderAttachment().toString());
}
}
类加载器的委托机制---分为两个过程
①类加载器的选择:当java虚拟机要加载一个类时,到底派出那个类加载器去加载?
(1).首先当前线程的类加载器去加载线程中的第一个类。
(2).如果类A中引用了B类,java虚拟机将使用类A的加载器来加载类B
(3).还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
②类加载器怎样加载:每个类加载器加载类时,又先委托给器上级类加载器。当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不再去找发起者类加载器的儿子,因为没有getChild方法;即使有,那么有多个儿子,到底要找那个一个呢?
思考:能否自己写一个JDK中已经存在的类呢?
通常不可以,因为根据类加载器的委托机制,每次都会先委托给上级寻找,先找到的是Jdk中的java.lang.System,而不是自己写的 java.lang.System类;但可以自己写一个类加载器,撇开器委托机制,不给它指定上级,来加载自己的类。
注意:需要说明一下 Java 虚拟机是如何判定两个Java类是相同的。Java虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。
编写自己的类加载器,模板方法设计模式:
父类->loadClass/findClass/得到class文件的转换成字节码用父类的defineClass方法。
子类:(自己干)
子类2:(自己干)
总体的流程已经在父类定义好,实现的具体细节有一部分父类不清楚,留给子类完成。
知识讲解:
1.自定义的类加载器必须继承ClassLoader
2.loadClass方法与findClass方法:只需重新findClass方法,就会跳出委托机制。
3.defineClass方法。
编程步骤:
1.编写一个队文件内容进行监督加密的程序
2.编写了一个自己的类加载器,可实现对加密的类进行撞在和解码。
3.编写一个程序调用类加载器加载类,在源程序中不能用该类定义引用变量,因为编程器无法识别这个类。程序中可以除了使用ClassLoader.load方法外,还可以使用使用设置线程的上下文加载器或者系统加载器,然后再使用Class.forName.
[java] view
plaincopy
package study.day2;
//需要加载的类
import java.util.Date;
public class ClassLoaderAttachment extends Date {
@Override
public String toString() {
return "hello,itcast";
}
}
//包含加密解密方法和覆盖ClassLoader的findClass方法
package study.day2;
import java.io.*;
import com.sun.beans.finder.ClassFinder;
public class MyClassLoader extends ClassLoader{
public static void main(String[] args) throws Exception {
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\'));
String destPath = destDir+destFileName;
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
//加密方法
public static void cypher(InputStream fis, OutputStream fos) throws Exception {
int b = -1;
while((b=fis.read())!=-1){
fos.write(b ^ 0xff);
}
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classFileName = classDir+"\\"+name.substring(name.lastIndexOf('.')+1)+".class";
try {
System.out.println("It's MyClassLoader.");
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte[] bys = bos.toByteArray();
return defineClass(null, bys, 0,bys.length);
}
catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
}
//调用写好的类加载器
package study.day2;
import java.util.Date;
public class ClassLoderTest {
public static void main(String[] args) throws Exception {
Class clazz = new MyClassLoader("itcastlib").loadClass("study.day2.ClassLoaderAttachment");
Date dd = (Date) clazz.newInstance();
System.out.println(dd);
}
}
关于类加载器的一个高级问题
当把我们写好的MyServlet作为jar包导入的Tomcat使用的JDK的jre/lib/ext中时,这时的MyServlet的类加载器由原来tomcat的WebAppClassLoader变成了ExtClassLoader,在加载TMyServlet时,又用到了HttpServlet,ExtClassLoader找不到就不会报错此时,只需将tomcat中包含HttpServlet的servlet-api.jar也导入ext文件夹下即可。
------ ASP.Net+Android+IO开发.Net培训期待与您交流! ------
总结内容来源于张孝祥老师的Java高新技术
内省IntroSpector→JavaBean
内省(Introspector)是java语言对Bean类属性、事件的一种缺省处理方法。
JavaBean主要用于传递数据信息,其方法用于访问私有变量,且方法名符合某种规则。
例如 : 类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name 属性,这就是JavaBean默认的规则。
内省主要是对JavaBean进行操作。JavaBean内部的方法要按照某种规则命名,JavaBean可以作为普通类进行操作;普通类如果内部有set()、get()方法,也可以当做JavaBean使用。
JavaBean的属性是通过get()和set()方法推断出来的,即去掉get、set后的字母,例如,一个类有方法getAge( )和setAge( ) , 那么bean的属性为age,而不是成员变量,因为成员变量是private看不见的。
获得属性名的规则:如果属性名的第二个字母是小写,则把第一个字母小写。例如,gettime—>time,setTime—>time,getCPU—>CPU。
PropertyDescriptor类
PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:
1、getPropertyType( ),获得属性的Class对象。
2、getReadMethod( ),获得用于读取属性值的方法;getWriteMethod( ),获得用于写入属性值的方法。
3、hashCode( ),获取对象的哈希值。
4、setReadMethod( ),设置用于读取属性值的方法;setWriteMethod( ),设置用于写入属性值的方法;
通过属性名获取对应的值,利用反射方法 ;
ElementBean e = new ElementBean(3,7);
String pn = "x";
PropertyDescriptor pd = new PropertyDescriptor(pn, e.getClass());
Method methodGet = pd.getReadMethod();//getReadMethod()对应get方法
Object retValX = methodGet.invoke(e);
System.out.println(retVal)
给某个属性设置值 :
ElementBean e = new ElementBean(3,7);
String propertyName = "x";
PropertyDescriptor pd = new PropertyDescriptor(pn,e.getClass());
Method methodSetX = pd.getWriteMethod();//getWriteMethod()对应set方法
Object value = 7;
methodSetX.invoke(e, value);
eclipse快速创建get()和set()方法:
右键→Source→Generate Geters and Setters,创建get()和set()方法。
eclipse把一些代码生成方法,提高复用性
选择一些代码,右键→Refactor→Extract Method
Introspector类
将JavaBean中的属性封装起来进行操作。在程序把一个类当做JavaBean来看,就是调用Introspector.getBeanInfo() 方法,得到的BeanInfo对象封装了把这个类当做JavaBean看的结果信息,即属性的信息。需要导包java.beans.*。
getPropertyDescriptors(),获得属性的描述,可以采用遍历BeanInfo的方法,来查找、设置类的属性。
代码
[java] view
plaincopy
private static Object getProperties_2(ElementBean e, String propertyName)throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
BeanInfo beanInfo = Introspector. getBeanInfo(e.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor p : pds){
if(p.getName().equals(propertyName)){
Method methodGet = p.getReadMethod();
retVal = methodGet.invoke(e);
}
}
return retVal;
}
通过这PropertyDescriptor类和Introspector类的比较可以看出,都是需要获得PropertyDescriptor,只是方式不一样:前者通过创建对象直接获得,后者需要遍历,所以使用PropertyDescriptor类更加方便。
BeanUtils工具包 , 为JavaBean提供更多、放方便的功能 .
使用时需要配合acpche提供的日志包logging一起使用 .
可以通过BuildPath,添加额外的jar包,或者工程下建立lib目录,将jar包复制进来,再加载这个jar包:右键→add to BuildPath。使用时需要导包:org.apache.commons.beanutils.BeanUtils。
下载的BeanUtils
beanutils.jar = beanutils-core.jar + beanutils-bean-collections.jar
获得属性的值,例如,BeanUtils.getProperty(pt1,"x"),返回字符串
设置属性的值,例如,BeanUtils.setProperty(pt1,"y",22)
将一个对象的属性值复制到另一个对象的属性 , copyProperties(Object dest, Object orig),需要保证属性一致。
BeanUtils的特点:
1、对基本数据类型的属性的操作:在WEB开发、使用中,录入和显示时,值会被转换成字符串,但底层运算用的是基本类型,这些类型转到动作由BeanUtils自动完成。
2、对引用数据类型的属性的操作:
首先在类中必须有对象,不能是null,
例如 : private Date birthday=new Date();。
操作的是对象的属性而不是整个对象
例如 : BeanUtils.setProperty(pt1,"birthday.time",121);
Java7的新特性:
1、Map和JavaBean之间可以进行相互转换,key是属性,value是属性的值。
方法describe( bean ):把JavaBean转换成Map;
方法populate( bean , map ):把Map封装成JavaBean ;
2、Map的简化书写形式 , 如下
Map map = (name:Kim,age:18);
BeanUtilssetProperty( )方法可以把Map看作成Bean ,
例如 : BeanUtils.setProperty(map,"name","Kim");
PropertyUtils类
和BeanUtils不同在于,运行getProperty、setProperty操作时,没有类型转换,使用属性的原有类型或者包装类。
全篇代码 :
[java] view
plaincopy
package com.itheima;
import java.beans.*;
import java.lang.reflect.*;
import java.util.Map;
import org.apache.commons.beanutils.*;
public class IntroSpectorTest {
@SuppressWarnings({ "unused", "rawtypes" })
public static void main(String[] args) throws Exception{
ElementBean e = new ElementBean(3,7);
String propertyName = "x";
Object value = 7;
//使用自己写的方法获取"x"的值
System. out.println( getProperties(e, propertyName));
System. out.println( getProperties_2(e, propertyName));
//使用BeanUtils设置"x"并获取其值
BeanUtils. setProperty(e, "x", "9");
System. out.println(BeanUtils. getProperty(e, propertyName));
//使用BeanUtils设置并获取属性birthday的值
BeanUtils. setProperty(e, "birthday.time", 1111);
System. out.println(BeanUtils. getProperty(e, "birthday.time"));
Map map = BeanUtils. describe(e);
System. out.println(map);
}
public static void setProperties(ElementBean e, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName,e.getClass());
Method methodSetX = pd.getWriteMethod();
methodSetX.invoke(e, value);
}
public static Object getProperties(ElementBean e, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, e.getClass());
Method methodGet = pd.getReadMethod();
Object retValX = methodGet.invoke(e);
return retValX;
}
public static Object getProperties_2(ElementBean e, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
BeanInfo beanInfo = Introspector. getBeanInfo(e.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor p : pds){
if(p.getName().equals(propertyName)){
Method methodGet = p.getReadMethod();
retVal = methodGet.invoke(e);
}
}
return retVal;
}
}
注解Annotation
JDK1.5出现的新特性。在java.lang.annotation包中。
对于过时的语句,java会提示过时了,通过@SuppressWarnings("Deprecation")在DOS中取消提示,但Eclipse无 法取消。这就是注解 .
一个注解就是一个类。
@SuppressWarnings,取消警告。
@Deprecated, 这个方法或类不再建议使用 , 在新版本中有其他方法或类可以代替这个使用,以后的版本也不会再更新这个方法或类 .
HashSet集合中,对象必须覆盖Object类的equals()方法,否则会继续使用Object类的equals()方法进行比较,错误的比较方 法。覆盖equals()方法,参数必须一致,为了防止错误写入本类的对象,加入-------->
@Override,必须正确覆盖父类方法,不是创建新方法。
总结 : 注解相当于一种标记。编译器、开发工具、javac通过反射获得注解里的内容,进而明确应该做什么、不应该做什么。注解可以加在包、类、属 性、方法、参数及局部变量之上。
注解的应用
在源程序中,调用一个类,如果这个类会用到注解,就需要先准备好注解类,然后类再调用注解类的对象 . 注意 : 类上的注解都是调用的注解的对象 , 而非注解类 . 可以加多个注解 .
注解类的写法类似接口,@interface。先写好注解类A, 将注解放在类B中,类C在调用类B时通过反射获得注解类A,进而明确需要做什么。
main()方法必须放在一个类下,但与这个类不一定有所属关系。
在注解类A上加注解B,这个注解B只为这个注解类A服务,B称为“元注解”。类似的还有元信息、元数据。
元注解有2个:Rentention和Target。对注解类的注解,可以理解为注解类的属性。
Rentention元注解
注解的生命周期:Java源文件—》class文件—》内存中的字节码。编译或者运行时,都有可能会取消注解。Rentention的3种取值意味让注解 保留到哪个阶段,RententionPolicy.SOURCE、RententionPolicy.CLASS(默认值)、 RententionPolicy.RUNTIME。
@Override、@SuppressWarnings是默认保留到SOURCE阶段;@Deprecated是保留到RUNTIME阶段。
Rentention相当于注解类的一个属性,因为Rentention的值不同,注解类保留到的阶段不同。注解类内部Rentention的值使用value表示,例如,@Deprecated中,value=Runtime。
Rentention的值是枚举RententionPolicy的值,只有3个:SOURCE、CLASS、RUNTIME。
Target元注解
性质和Rentention一样,都是注解类的属性,表示注解类应该在什么位置,对那一块的数据有效。例如,@Target(ElementType.METHOD)//表示在方法上有效
Target内部的值使用枚举ElementType表示,表示的主要位置有:注解、构造方法、属性、局部变量、函数、包、参数和类(默认值)。表示多个位置有效可以使用数组,例如,@Target({ElementType.METHOD,ElementType.TYPE})。
类、接口、枚举、注解这一类事物用TYPE表示,Class的父类,JDK1.5的新特性。
注解Annotation的属性
注解的基本属性
属性可以给注解提供更加详细的信息。
注解相当于接口,属性相当于接口的方法。例如,@ItcastAnnotation(color="black"),注解ItcastAnnotation中必须有String color();语句 , 给属性赋值,取值时类似调用方法例如,System.out.println(annotation.color()) ;
如果只有value属性,没有其他属性,可以不写=,只针对value,例如,@SuppressWarnings("Deprecation")。或者 有其他属性而且有缺省值,例如,String color() default "blue";,此时value单独出现,不用value=。
注意 : 引用注解时,所有的属性必须全部出现,除非属性有缺省值。
获得注解的属性的值,例如
if(AnnotationDemo.class.isAnnotationPresent(ItcastAnnotation.class)){
ItcastAnnotation annotation =(ItcastAnnotation)AnnotationDemo.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.color());
}
}
利用反射获得注解的对象,在让该对象调用属性对应的方法。注意类型转换。
Rentention和Target也是属性,都是value对应的值,值的类型分别是RententionPolicy和ElementType,例如,@Retention(value=RetentionPolicy.RUNTIME)。
注解的高级属性
给注解增加高级属性,数组、枚举、注解。
数组类型的属性
例如,int[] arr() default {3,7,5};,MyAnnotation(arr={3,7,6})。如果数组只有1个元素,可以不加{}。 @Target({ElementType.METHOD,ElementType.TYPE})也是数组类型的属性。
枚举类型的属性
//注解类内部的内容
EnumerationTest2.TrafficLamp lamp() default EnumerationDemo.TrafficLamp.RED;
//调用注解的类上
@ItcastAnnotation(lamp=EnumerationDemo.TrafficLamp.YELLOW)
//对注解进行操作
System.out.println(annotation.lamp().nextLamp());
注解类型的属性
将一个注解类作为属性加入到另一个注解类中。
MetaAnnotation annotationAtt() default @MetaAnnotation("Jobs")
@ItcastAnnotation(annotationAtt=@MetaAnnotation("lmn"))
annotation.annotationAtt().value()
注解的返回值可以是8个基本类型、String、Class、枚举以及前面类型的数组,内部还有属性。
需要详细学习注解,可以通过java语言规范,即languagespecification。
注解类的代码:
[java] view
plaincopy
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解的注解又叫元注解
@Retention(RetentionPolicy.RUNTIME )//决定注解的生命周期
@Target({ElementType. TYPE,ElementType. METHOD}) //决定注解能放在什么位置
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {1,2,3};
EnumTest2.Trafficlamp lamp() default EnumTest2.Trafficlamp. RED;
MetaAnnotation annotationAttr() default @MetaAnnotation( "xyz");
Class classAttr();
}
调用注解类的代码:
package com.itheima;
@ItcastAnnotation(value="abc" ,annotationAttr=@MetaAnnotation ("lmn" ),classAttr=String.class)
public class AnnotationTest {
@ItcastAnnotation(value= "abc",classAttr=String. class)
public static void main(String[] args) {
System. runFinalizersOnExit( true);
Class classAT = AnnotationTest.class;
Class classIA = ItcastAnnotation.class;
if( classAT.isAnnotationPresent(classIA)){
ItcastAnnotation ia = (ItcastAnnotation)classAT.getAnnotation(classIA);
System. out.println(ia.color());
System. out.println(ia.value());
System. out.println(ia.arrayAttr(). length);
System. out.println(ia.lamp().nextTra());
System. out.println(ia.annotationAttr().value());
System. out.println(ia.classAttr());
}
}
@Deprecated//已过时,这个方法或类不再建议使用
public void sayHello(){
System. out.println( "hi,传智播客");
}
}
泛型 Generic
一.问题的引入:为什么要用泛型?
引入泛型的原因之一:在使用集合时,可以向定义好的某一集合中存入任意类型的数据,而我们希望整个集合中的类型都是一样的,当加入的不期望的类型后,只有到运行期才能发现。 而且取出的类型需要强制转换。
[java] view
plaincopy
//没有使用泛型:
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
// int i = (Integer)collection.get(1);//这句会报错</span>
//使用泛型的代码:
// 在集合中的应用
ArrayList<String> collection1 = new ArrayList<String>();
// collection1.add(1);
// collection1.add(1L);
collection1.add("String");
String i = collection1.get(0);// 在反射中的应用
//用反射实现操作:String str = new String(new StringBuffer("abc"));
Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);
String str1 = constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str1.charAt(2));
从上面两段代码的对比中,很容易看出使用泛型的好处:1.限定了集合元素的类型;2.省去了强制转换的过程。
JDK升级一般可分为三个大的方面:1.简化书写;2.提高效率;3.提高安全性。而泛型就属于其中的第三方面安全,它是一种安全机制。
二.利用反射穿透泛型限制
泛型能绝对保证集合中存入数据都是它限定的类型吗?
[java] view
plaincopy
ArrayList<Integer> collection2 = new ArrayList<Integer>();
System.out.println(collection1.getClass()==collection2.getClass());
collection2.add(“123”);//这句会报错
collection2.getClass().getMethod("add", Object.class).invoke(collection2, "hello");
System.out.println(collection2.get(0)); //结果却为hello
三.ArrayList<E>类定义和ArrayList<Integer>类引用中涉及的术语:
1.ArrayList<E>:这个整体称为“泛型类型”,其中的“E”称为 类型变量 或 类型参数 。
2.ArrayList<Integer>:整体称为“参数化的类型”,“Integer”称为“类型参数的实例”或“实际类型参数”,"<>"读为typeof,ArrayList称为“原始类型”。
3.参数化类型与原始类型的兼容性:可以互相引用,但编译器会报告警告。
4.参数化类型不考虑类型参数的继承关系
5.编译器不允许创建类型变量的数组。在创建数组实例时,数组的元素不能使用参数化的类型。
[java] view
plaincopy
Collection<String> c = new Vector();
Collection c1 = new Vector<String>();
// 见第4条
//错误语句 Vector<String> v=new Vector<Object>();
//错误语句 Vector<Object> v1=new Vector<String>();
Vector v2=new Vector<String>();
Vector<Object> v3 = v2;
//错误语句 Vector <Integer> [] vectorList = new Vector <Integer>[10];//见第5条。
Vector <Integer[]> vectorList = new Vector <Integer[]>();
四.扩展应用:泛型中的通配符“?”
1.限定通配符的上边界
Vector<? extends Number> x = new Vector<Integer>();正确的
Vector<? extends Number> x = new Vector<String>():错误的。
2.限定通配符的下边界
Vector<? super Integer> x = new Vector<Number>();正确的。
Vector<? super Integer> x = new Vector<Byte>();错误的。
注意:限定通配符总是包括它自己。
[java] view
plaincopy
printCollection_2(collection2);
// Class<Number> x = String.class.asSubclass(Number.class);//错误。
// Class x1 = String.class.asSubclass(Number.class);
// Class<String> y = Class.forName("java.lang.String");//错误
Class y = Class.forName("java.lang.String");//要抛出异常。
Class<?> y1;
Class<String> y2 = null;
y1=y2;
// y2=y1;
五.泛型集合类的综合应用案例
[java] view
plaincopy
HashMap<String,Integer> hm = new HashMap<String,Integer>();
hm.put("zyq", 18);
hm.put("zsy", 49);
hm.put("zyx", 25);
Set<Map.Entry<String,Integer>> entrySet = hm.entrySet();
for(Map.Entry<String, Integer> entry:entrySet){
System.out.println(entry.getKey() + ":" + entry.getValue());
}
六.定义泛型方法
1.泛型类似C++中的模板,但没有C++中的功能强大。
2.泛型的实际类型参数只能是引用数据类型,例如:对于HashMap<K,V>,K和V不能是八种基本数据类型。
3.异常也可以用泛型
4.类型参数的类型推断
例一:模拟C++中的模板实现求和功能(但没有C++中好用)
[java] view
plaincopy
add(1,2);
Number x1 = add(1,1.4);//int 和float的最大公共集是Number
Object x2 = add(1,"abc");//String 和int的最大公共集是Object
ivate static<T> T add(T x,T y){//取得的T是 x 和 y的最大公共集,其实也是java中多态的体现。
return null;
}
例二:实现数组中的元素位置交换
[java] view
plaincopy
swap(new String[]{"abc","123","hello"},0,1);
// swap(int[]{1,2,3,4},2,3);//int是基本数据类型,所以不能实现。
[java] view plaincopy
private static<T> void swap(T[] t ,int x,int y){
T temp = t[x];
t[x] = t[y];
t[y] = temp;
}
例三:
[java] view
plaincopy
ArrayList<Integer> al = new ArrayList<Integer>();
al.add((Integer)autoConvert(345));
System.out.println(al.get(0));
String s = autoConvert("123");
System.out.println(s);
fillArray(new Integer[]{1,3,4},1);
copy1(new Vector<String>(), new String[]{"123","5674"});
copy2(new Date[20], new String[10]);//这个是两个取交集。
// copy1(new Vector<Date>(), new String[]{"123","5674"});
//类型推断的传播性,Vector中T已经指定为Date,后边的T也应该是Date,否则报错。
public static<T> void fillArray(T[] t,T obj){
for(int x= 0;x<t.length;x++){
t[x]= obj;
}
}
public static <T> T autoConvert(Object obj){
return (T)obj;
}
public static <T>void copy1(Collection<T> des ,T[] src){
}
public static <T>void copy2(T[] des ,T[] src){
}
七、泛型类
[java] view
plaincopy
import java.util.Set;
//dao data access object:CRUD
public class GenericDao<E> {
//增
public void add(E x){
}
//删
public void delete(int id){
}
public void delete(E obj){
}
//查
public E findByID(int id){
return null;
}
public E findByName(String name){
return null;
}
public Set<E> findByConditions(String where){
return null;
}
//改
public void update(E obj){
}
public static<E> void update1(E obj){//静态方法不能用类上定义的泛型参数,但可以自己定义泛型,这里的E和类上的E是两码事。
}
}
GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
dao.add(new ReflectPoint(5, 7));
// String s = dao.findByID(1);//返回的也必须是ReflectPoint
八、通过反射获得泛型的参数化类型
没办法通过泛型的引用,用反射 获取参数化类型的类型参数。
但可以通过反射获得方法的参数列表,从参数列表中获取参数化类型的原始类型和实际类型参数
[java] view
plaincopy
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;
class GenericTest {
public static void main(String[] args) throws Exception{
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types =applyMethod.getGenericParameterTypes();
ParameterizedType pType =(ParameterizedType) types[0];
System.out.println(pType.getClass());
System.out.println(pType.getOwnerType());
System.out.println(pType.getRawType());
System.out.println(pType.getActualTypeArguments()[0]);
}
public static void applyVector(Vector<Date> v){
}
/*
public static void applyVector(Vector<Integer> v){ //这个方法和上边的方法是同一个方法。
}*/
}
类加载器
什么叫类加载器? 加载类的工具.
java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个负责特定位置的类.
BootStrap xitClassLoader AppClassLoader
类加载器也是java类,因为其他是java类的类加载器也要被类加载器加载,显然必须有第一个类加载器不是java类, 这正是:BootStrap,它是由C++编写的.
java虚拟机中类加载器之间的父子关系和管辖范围:
Bootstrap ClassLoader ---->JRE/lib/rt.jar
↑
Extension ClassLoader ---->JRE/lib/ext/.jar
↑
ApplicationClassLoader ---->ClassPath指定的所有jar或目录。
↑
MyClassLoader ---->指定的特殊目录
↑
itcastClassLoader---->指定的特殊目录
[java] view
plaincopy
package study.day2;
import java.util.Date;
public class ClassLoderTest {
public static void main(String[] args) throws Exception {
System.out.println(ClassLoderTest.class.getClassLoader().getClass().getName());
System.out.println(System.class.getClassLoader());
ClassLoader loader = ClassLoader.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
System.out.println(new ClassLoaderAttachment().toString());
}
}
类加载器的委托机制---分为两个过程
①类加载器的选择:当java虚拟机要加载一个类时,到底派出那个类加载器去加载?
(1).首先当前线程的类加载器去加载线程中的第一个类。
(2).如果类A中引用了B类,java虚拟机将使用类A的加载器来加载类B
(3).还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
②类加载器怎样加载:每个类加载器加载类时,又先委托给器上级类加载器。当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不再去找发起者类加载器的儿子,因为没有getChild方法;即使有,那么有多个儿子,到底要找那个一个呢?
思考:能否自己写一个JDK中已经存在的类呢?
通常不可以,因为根据类加载器的委托机制,每次都会先委托给上级寻找,先找到的是Jdk中的java.lang.System,而不是自己写的 java.lang.System类;但可以自己写一个类加载器,撇开器委托机制,不给它指定上级,来加载自己的类。
注意:需要说明一下 Java 虚拟机是如何判定两个Java类是相同的。Java虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。
编写自己的类加载器,模板方法设计模式:
父类->loadClass/findClass/得到class文件的转换成字节码用父类的defineClass方法。
子类:(自己干)
子类2:(自己干)
总体的流程已经在父类定义好,实现的具体细节有一部分父类不清楚,留给子类完成。
知识讲解:
1.自定义的类加载器必须继承ClassLoader
2.loadClass方法与findClass方法:只需重新findClass方法,就会跳出委托机制。
3.defineClass方法。
编程步骤:
1.编写一个队文件内容进行监督加密的程序
2.编写了一个自己的类加载器,可实现对加密的类进行撞在和解码。
3.编写一个程序调用类加载器加载类,在源程序中不能用该类定义引用变量,因为编程器无法识别这个类。程序中可以除了使用ClassLoader.load方法外,还可以使用使用设置线程的上下文加载器或者系统加载器,然后再使用Class.forName.
[java] view
plaincopy
package study.day2;
//需要加载的类
import java.util.Date;
public class ClassLoaderAttachment extends Date {
@Override
public String toString() {
return "hello,itcast";
}
}
//包含加密解密方法和覆盖ClassLoader的findClass方法
package study.day2;
import java.io.*;
import com.sun.beans.finder.ClassFinder;
public class MyClassLoader extends ClassLoader{
public static void main(String[] args) throws Exception {
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\'));
String destPath = destDir+destFileName;
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
//加密方法
public static void cypher(InputStream fis, OutputStream fos) throws Exception {
int b = -1;
while((b=fis.read())!=-1){
fos.write(b ^ 0xff);
}
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classFileName = classDir+"\\"+name.substring(name.lastIndexOf('.')+1)+".class";
try {
System.out.println("It's MyClassLoader.");
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte[] bys = bos.toByteArray();
return defineClass(null, bys, 0,bys.length);
}
catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
}
//调用写好的类加载器
package study.day2;
import java.util.Date;
public class ClassLoderTest {
public static void main(String[] args) throws Exception {
Class clazz = new MyClassLoader("itcastlib").loadClass("study.day2.ClassLoaderAttachment");
Date dd = (Date) clazz.newInstance();
System.out.println(dd);
}
}
关于类加载器的一个高级问题
当把我们写好的MyServlet作为jar包导入的Tomcat使用的JDK的jre/lib/ext中时,这时的MyServlet的类加载器由原来tomcat的WebAppClassLoader变成了ExtClassLoader,在加载TMyServlet时,又用到了HttpServlet,ExtClassLoader找不到就不会报错此时,只需将tomcat中包含HttpServlet的servlet-api.jar也导入ext文件夹下即可。
------ ASP.Net+Android+IO开发.Net培训期待与您交流! ------
相关文章推荐
- 黑马程序员 Java自学总结十八 Java高新技术第一天
- 黑马程序员 Java自学总结二十 Java高新技术第三天
- 黑马程序员java自学总结之--网络编程
- 黑马程序员_java高新技术总结【1】(枚举和部分java新特性)
- 黑马程序员----------Android自学第二天总结(网络下载和解析)
- 黑马程序员 Java自学总结六 面向对象三个特征之封装
- 黑马程序员__JAVA高新技术--反射、注解总结
- 黑马程序员——java第二天:基础语法(总结:运算符)
- 黑马程序员java自学总结之--数据关系模型和对象模型
- 黑马程序员 Java自学总结七 面向对象三个特征之继承
- 黑马程序员 Java自学总结十一 线程
- 黑马程序员java自学总结之--异常
- 黑马程序员——高新技术---Java基础-集合特点和数据结构总结
- 黑马程序员 Java自学总结十二 String类和基本数据类型包装类
- 黑马程序员-Java基础学习第二天总结
- 黑马程序员java自学总结之--集合
- 黑马程序员---Java高新技术反射知识点总结
- 黑马程序员java自学总结--java基础
- 黑马程序员 Java自学总结一 Java语言的基础知识
- 黑马程序员__JAVA高新技术--枚举及内省JavaBean、可变参数等知识点总结