疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java基础:基础加强


 

 
1.泛型
 
1.1 概述
(1).泛型是JDK1.5版本以后出现的新特性,用于解决安全文帝的一个类型安全机制;
(2).泛型在集合类中的应用:JDK1.5以后的集合类希望在定义集合时,明确表明要向集合中存储的是哪一类型的数据,无法处理指定类型以外的数据;
(3).泛型是提供给Javac编译器使用的,可以限定集合中的输入类型,在编译完成后的字节码文件中会去掉类型信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型一致;
(4).由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据;
使用反射的方式绕过编译器,添加不同类型的数据
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//绕过编译器向泛型集合中添加不同元素;  
package com.zr.day28;  
import java.lang.reflect.*;  
import java.util.*;  
class GenericDemo  
{  
    public static void main(String[] args) throws Exception  
    {  
        ArrayList<String> al = new ArrayList<String>();  
        al.add("generic");  
        //编译失败,不能添加Integer类型;  
        //al.add(Integer.valueOf(5));  
        System.out.println(al);  
  
          
        //通过反射来添加不同类型的数据;  
        Method addMethod = al.getClass().getMethod("add", Object.class);  
        addMethod.invoke(al, Integer.valueOf(8));  
          
        System.out.println(al);  
    }  
}  
 
 
(5).当仍然按照JDK1.5之前的方式处理,即不定义泛型类型,编译器会出现Uncheck警告;
1.2 泛型的好处
(1).将运行时期出现的问题ClassCastException,转移到了编译时期,方便程序员解决问题,让运行时期问题减少安全;
(2).避免了强制转换的麻烦;比如在反射的应用中;
1.3 泛型中的术语
以ArrayList<E>和ArrayList<Integer>为例:
整个ArrayList<E>称为泛型类型;
ArrayList<E>中的E称为类型变量或类型参数;
整个ArrayList<Integer>称为参数化的类型;
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数;
ArrayList<Integer>中的<>称作typeof;
ArrayList称为原始类型;
参数化类型和原始类型的兼容性:
(1).参数化类型可以引用一个原始类型的对象,编译报告警告
eg:Collection<String> c = new Vector();
(2).原始类型可以引用一个参数化类型的对象,编译报告警告
eg:Collection c = new Vector<String>();
参数化类型不考虑类型参数的继承关系;
错误示例:
Vector<String> v = new Vector<Object>();
Vector<Object> v = new Vector<String>();
编译器不允许创建泛型变量的数组;
1.4 泛型的通配符
通配符:<?>,又称占位符;
当传入的类型不确定时,可以使用通配符,好处是可以不用明确具体传入的类型,这样在使用泛型类或者泛型方法时,提高了扩展性;
使用<?>通配符可以引用其他各种参数化的类型,<?>通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法;
泛型限定:
对于一个范围的一类事物,可以通过泛型限定的方式定义;
(1).? extends E:可以接收E类型或者是E类型的子类类型,称为上限限定;
(2).? super E:可以接收E类型或者E类型的父类类型,称为下限限定;
示例:定义一个上限限定的方法:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.zr.day28;  
import java.lang.reflect.*;  
import java.util.*;  
class Person  
{  
    protected String name;  
    Person(String name)  
    {  
        this.name = name;  
    }  
    public String toString()  
    {  
        return "person:"+this.name;  
    }  
}  
class Student extends Person  
{  
    Student(String name)  
    {  
        super(name);  
    }  
    public String toString()  
    {  
        return "student:"+this.name;  
    }  
}  
class GenericDemo  
{  
    public static void main(String[] args)  
    {  
        ArrayList<Person> al_p = new ArrayList<Person>();  
        al_p.add(new Person("zxx"));  
        al_p.add(new Person("flx"));  
        al_p.add(new Person("lhm"));  
        ArrayList<Student> al_s = new ArrayList<Student>();  
        al_s.add(new Student("zr"));  
        al_s.add(new Student("fyk"));  
        al_s.add(new Student("ggx"));  
          
        outArraylist(al_p);  
        System.out.println("***********");  
        outArraylist(al_s);  
    }  
    //定义上限限定方法;  
    public static void outArraylist(ArrayList<? extends Person> al)  
    {  
        Iterator<? extends Person> it = al.iterator();  
        while(it.hasNext())  
        {  
            System.out.println(it.next());  
        }  
    }  
}  
 
 
1.5 泛型类
当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来实现扩展;
如果类的实例对象中的多出都要用到同一泛型参数,即这些地方引用的泛型类型要保持同一个世纪类型时,可以采用泛型类型的方式进行定义,也就是类级别的泛型;
泛型类是根据引用该类名时指定的类型信息来参数化类型变量的;
泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所要操作的类型就已经固定了;
注意:
(1).在对泛型类型进行参数化时,类型参数的实力必须是引用类型,不能是基本数据类型;
(2).当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和静态方法调用:因为静态成员是被所有参数化的类所共享,无哦一静态成员不应该有类级别的类型参数;
1.6 泛型方法
为了让不同的方法可以操作不同类型的数据,而且类型不确定,可以将泛型定义在方法上;
泛型方法的使用:
(1).用于放置泛型类型的尖括号定义在方法的修饰符之后和方法的返回类型之前;
(2).只有引用类型才能作为泛型方法的实际参数,eg:swap(new int[5],3,4)编译失败;
(3).可以使用&符号来定义多个泛型边界;
(4).普通方法,构造方法,静态方法中都可以使用泛型,编译器不允许创建类型变量的数组;
(5).在泛型中可以同时有多个类型参数,参数之间用逗号隔开;
(6).在类和方法上都定义了泛型的时候,方法所操作的类型不受类上定义的泛型局限;
(7).静态方法泛型:静态方法不能访问类上定义的泛型,因为只有在类的对象建立时,泛型才会确定;如果静态方法操作的类型不确定的话,可以经泛型定义在方法上;
1.7 类型的推断
编译器判断泛型方法的实际类型参数的过程称为类型推断;
(1).当某个类型变量只在真个参数列表中的所有参数和返回值中的一处被应用,那么根据调用方法时该处的实际应用类型来确定;
(2).当某个类型变量在整个参数列表中的所有参数和返回值中的多出被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型;
(3).当某个类型变量在整个参数列表中的所有参数和返回值中的多出被应用了,如果调用方法时这多处的实际应用类型对应不同类型,且没有使用返回值,这时候取多个参数中的最大交集类型;
(4).当某个类型变量在整个参数列表中的所有参数和返回值中的多出被应用了,如果调用方法时这多处的实际应用类型对应不同类型,并且使用返回值,这时优先考虑返回值的类型;
(5).参数类型的类型推断具有传递性;
1.8 通过反射获得泛型的实际类型参数
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//演示泛型的反射,获取泛型的实际类型参数;  
package com.zr.day28;  
import java.lang.reflect.*;  
import java.util.*;  
class GenericDemo  
{  
    public static void main(String[] args) throws Exception  
    {  
        Method applyMethod = GenericDemo.class.getMethod("applyVector", Vector.class);  
        //获取方法的参数列表带泛型;  
        Type[] types = applyMethod.getGenericParameterTypes();  
        System.out.println(types[0]);  
        //ParameterizedType:表示参数化类型;其超类是Type类型;  
        ParameterizedType pt = (ParameterizedType)types[0];  
        //返回表示此类型实际类型参数的 Type对象的数组;  
        System.out.println(pt.getActualTypeArguments()[0]);  
        //表示声明此类型的类或接口;  
        System.out.println(pt.getRawType());  
    }  
      
    //在编译后,字节码文件中没有泛型信息,就不能通过class文件来获取泛型;  
    //当方法中定义的参数有使用泛型的时候,可以通过对方法的反射获取泛型信息;  
    public static void applyVector(Vector<Date> v)  
    {  
          
    }  
}  
 
 
2.枚举
 
2.1 枚举概述
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错;
枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标;
使用普通类模拟枚举:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
/* 
 * 使用普通类模拟枚举; 
 * 1.将构造方法私有化,不允许本类以外的其他地方创建对象; 
 * 2.每个元素分别用一个公有的静态成员变量表示; 
 * 3.可以有若干共有方法;采用抽象方法定义特性方法,让对象在创建时去具体实现,优化大量的if/else语句; 
 * */  
package com.zr.day25;  
//模拟一个星期类,类中的元素固定  
//因为包含抽象方法,所以类定义为抽象类  
abstract class WeekDay  
{  
    //构造方法私有化  
    private WeekDay(){}  
    //将每个元素需要实现的特性方法定义抽象方法  
    public abstract WeekDay nextDay();  
    //类中的成员变量在创建时要实现nextDay()方法  
    public static final WeekDay MON = new WeekDay(){  
        public WeekDay nextDay()  
        {  
            return SUN;  
        }  
    };  
    public static final WeekDay SUN = new WeekDay(){  
        public WeekDay nextDay()  
        {  
            return MON;  
        }  
    };  
    public String toString()//自定义打印输出的结果;  
[java] view plain copy 在CODE上查看代码片派生到我的代码片
{  
[java] view plain copy 在CODE上查看代码片派生到我的代码片
        return this==SUN?"SUN":"MON";  
    }  
}  
 
 
2.2 枚举的基本应用
(1).通过关键字定义枚举类,枚举类是一个特殊的类,每个元素都是该类的一个实例对象;
(2).用枚举约定值,以后用此类型定义的值只能是这个类中规定好的那些值,若不是这些值,编译器会报错;
(3).在编译时期会发现错误,表明值不符合条件,减少了运行时期的错误;
(4).枚举类是一个类,而且是一个不可被继承的final类,其中的元素都是类静态常量;
(5).在类中定义的枚举类似枚举内部类,可以被成员修饰符修饰;
(6).只要是使用到了枚举类,就会创建枚举所包含的实例对象;
(7).用枚举可以实现单例;
(8).代码格式上,枚举元素必须位于枚举体中的最开始部分,枚举元素列表后面要有分号和其他成员分隔,当没有其他成员时可以省略分号;
(9).枚举中的常用方法:
构造方法:
枚举的构造方法只是在构造枚举元素的时候被调用;
构造方法只能被private修饰,这样可以保证外部代码不会在重新创建枚举类型实例;
构造方法有多个,重载形式存在,具体的实例对象可以由相应的构造方法创建,不需要构造方法一致;
非静态方法:所有的枚举类都继承了Enum类,因此枚举对象不能在继承其他类;
String toString():返回枚举常量的名称;
String name():返回枚举常量的名称;
int ordinal():返回枚举常量的序数,在枚举声明中的位置,初始序数为0;
Class getClass():获取对应的类名;
静态方法:
values():获取所有的枚举对象元素;
valueOf(String e):转换为对应的枚举对象,将字符串转换为枚举对象;
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.zr.day25;  
class EnumDemo  
{  
    //定义一个表示星期信息的枚举类;  
    //枚举内部类  
    public enum  Week  
    {  
        //枚举的元素必须要放在枚举体的开头  
        MON {  
            @Override  
            public Week nextDay() {  
                return THU;  
            }  
        },THU {  
            @Override  
            public Week nextDay() {  
                return WED;  
            }  
        },WED {  
            @Override  
            public Week nextDay() {  
                return THR;  
            }  
        },THR {  
            @Override  
            public Week nextDay() {  
                return FRI;  
            }  
        },FRI {  
            @Override  
            public Week nextDay() {  
                return STA;  
            }  
        },STA {  
            @Override  
            public Week nextDay() {  
                return SUN;  
            }  
        },SUN {  
            @Override  
            public Week nextDay() {  
                return MON;  
            }  
        };  
        //如果枚举中还有成员:变量,方法,构造方法,用分号";"将枚举元素和其他成员分隔;  
        //枚举中定义了抽象方法,枚举元素在实例化的时候就需要实现抽象方法;  
        //同时构造方法可以有多个,根据需要让具体的元素实例调用相应的构造方法;  
        public abstract Week nextDay();  
    }  
      
    public static void main(String[] args)  
    {  
        //可以声明操作枚举的语句;  
    }  
}  
 
 
 
3.反射
 
3.1 反射技术:
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态的获取信息以及动态调用对象的成员方式称为Java语言的反射机制;
反射就是对Java类进行剖析,将Java类中的各种成分映射成相应的Java类;
反射大大的提高了程序的扩展性;
3.2 反射的使用:
一个已经可以使用的程序,不建议在对代码进行修改,当后期需要功能完善的时候该如何处理:
常用的做法是,提供一个配置文件,来供以后实现此程序的类来扩展个功能,让后期出现的子类直接将类名配置到配置文件中,然后处理:
(1).加载这个类;
(2).创建该类的对象;
(3).使用该类中的内容;
应用程序使用的类不确定时,可以通过提供配置文件,让使用者将具体的子类存储到配置文件中,然后该程序通过反射对指定的类进行内容的获取;
3.3 反射的基石:Class类
(1).Class概述:
所有的类文件都有共同的属性,可以进行向上抽取,把这些共性内容封装成一个类,这个类就叫Class:专门用于描述字节码文件的对象;
Class类描述的信息有:类的名字,类的访问属性,类所属的包,字段名称列表,方法名称列表,构造器列表;
字节码文件(二进制):当源程序中使用到某个类时,首先要将这个类的源文件编译成二进制代码,然后将这些二进制代码文件加载进内存;
(2).Class和class的区别:
Class指的是Java程序中的各个Java类是属于同一类事物,每一个Java类就是Class的一个实例;
class是类的实例对象;
(3).获取Class对象的方式:
方式一:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public static void main(String[] args)  
{  
    //获取Class对象的方式  
    Person p = new Person("zr",23);  
    //方式一:通过实例对象的getClass()方法获取;  
    //在获取时,需要具体的类和该类的对象,毫无扩展性而言;  
    Class clazz = p.getClass();  
}  
 
 
方式二:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public static void main(String[] args)  
{  
    //获取Class对象的方式  
    //方式二:类类型都具备一个静态的属性class,这个属性可以直接获取该类型对应的Class对象;  
    //相较方式一,不需要创建对象,不用调用getClass()方法  
    //但是还是要使用具体的类,以及该类中的一个具体的class属性;  
    Class clazz = Person.class;  
}  
 
 
方式三:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public static void main(String[] args) throws ClassNotFoundException  
{  
    //获取Class对象的方式  
    //方式三:只需要知道类的名称即可,不需要使用该类,也不需要调用具体的方法或属性;  
    //即使是该类不存在也没有关系,依旧可以根据名称获取,所以使用这种方式会出现异常ClassNotFoundException  
    //另外使用这种方式最好是将类名的全称包括包名填写完整;  
    //这种方式仅需要知道类的名称,更有利于扩展;  
    Class clazz = Class.forName("Person");  
}  
 
 
注意:
1).九个预定义的Class:byte,short,int,long,float,double,char,boolean以及void.class;
2).Integer.TYPE是Integer类的一个常量,它代表的是此包装类型包装的基本类型字节码,即Integer.TYPE=int.class;
3).只要是在源程序中出现的哦类型都有各自的Class实例对象,数组类型的实例对象,eg:int[].class,可以使用Class.isArray()方法来判断是否为数组类型;
(4).Class类中的常用方法
static class forName(String className):返回与带有给定字符串名的类或接口相关联的Class对象;
Constructor getConstructor(Class... parameterType):
Constructor getDeclaredConstructor(Class... parameterType):
Constructor[] getConstructors():
Constructor[] getDeclaredConstructors():
Method getMethod(String methodName, Class... parameterType)
Method getDeclaredMethod(String methodName, Class... parameterType)
Method[] getMethods()
Method[] getDeclaredMethods()
Field getField(String fieldName)
Field getDeclaredField(String fieldName)
Field[] getFields()
Field[] getDeclaredFields()
String getName():以字符串形式返回此Class对象所表示的实体名称;
package getPackage()
Class getSuperClass():返回表示此Class所表示的实体的超类的Class;
boolean isAnnotation():判断是否为注解类型;
boolean isArray():判断是否是数组类;
boolean isPrimitive():判断是否为一个基本数据类型;
Object newInstance():创建此Class对象所表示的类的一个实例,无参构造方法;
(5).通过Class对象创建具体类的实例;
之前的通用做法:
1).查找并加载class文件进内存,并将该文件封装成Class对象;
2).再依据Class对象创建该类具体的实例;
3).调用构造方法对对象进行初始化;
现在使用Class对象实例来实现:
1).查找并加载class文件进内存,并将该文件封装成Class对象;
2).获取该Class对象实例后,调用newInstance()方法,使用空参的构造方法进行初始化;
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.zr.day28;  
class Person  
{  
    private String name;  
    private int age;  
    Person()  
    {  
        System.out.println("person() run!");  
    }  
    Person(String name, int age)  
    {  
        System.out.println("person(string,int) run!");  
        this.name = name;  
        this.age = age;  
    }  
    public String toString()  
    {  
        return "name = " + this.name +"***age = "+this.age;  
    }  
}  
class ClassDemo  
{  
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException  
    {  
        //加载class文件,获取Class实例对象;  
        Class clazz = Class.forName("com.zr.day28.Person");  
        //调用该实例对象的newInstance()方法,使用无参的构造方法;  
        //需要进行强转,而且会抛出异常;  
        Person p = (Person)clazz.newInstance();  
        System.out.println(p);  
    }  
}  
 
 
3.4 构造方法的反射:Constructor
(1).Constructor概述
如果指定的类中没有空参的构造方法,或者要创建的类对象需要通过指定的构造方法进行初始化,这时直接使用Class实例对象的newInstance()方法就不适用;
既然要通过指定的构造方法进行对象的初始化,就必须先获得这个类的构造方法;
class类中的构造方法用Constructor类来说明;
(2).获取构造方法的方式:
Constructor[] getConstructors():获取类中的所有构造方法;公有的;
Constructor getConstructor(Class...  parameterType):获取某个具体的构造方法
(3).使用指定的构造方法创建实例对象
object newInstance(Object...  initargs)
使用反射方式创建实例时,newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致;
newInstance()方法没调用一次就构造一个对象;
使用Constructor类来创建实例的好处是可以指定构造函数,而Class类只能使用无参的构造函数创建类的实例对象;
示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.zr.day28;  
  
import java.lang.reflect.*;  
  
class Person  
{  
    private String name;  
    private int age;  
    public Person()  
    {  
        System.out.println("person() run!");  
    }  
    public Person(String name, int age)  
    {  
        System.out.println("person(string,int) run!");  
        this.name = name;  
        this.age = age;  
    }  
    public String toString()  
    {  
        return "name = " + this.name +"***age = "+this.age;  
    }  
}  
class ClassDemo  
{  
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException  
    {  
        //对构造方法的反射;  
        Constructor personCon = Class.forName("com.zr.day28.Person").getConstructor(String.class,Integer.TYPE);  
        //调用指定的构造方法,强制转换;  
        Person p = (Person)personCon.newInstance("zr",23);  
        System.out.println(p);  
    }  
}  
 
 
3.5 方法的反射:Method
(1).Method概述
Method类代表某个类中的成员方法,调用某个对象上的方法,要先得到方法,在针对某个对象调用;
使用反射的方式调用某个方法时,要指定具体的方法同时还要关联具体执行该方法的对象;
(2).获取类中的方法
Method[] getMethods():获取公共的方法其中包括父类的方法;
Method[] getDeclaredMethod();获取本类中的方法,包括私有的;
Method getMethod(String methodName, Class... parameterType):当方法的参数为空时,可以定义为null;
(3).执行某个具体的方法
Object  invoke(Object obj, Object... args)
其中obj就是调用这个方法的具体对象,如果第一个参数为null,说明Method对象对应的是一个静态方法;
示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//对String类中的方法进行反射,调用其中的方法;  
package com.zr.day28;  
import java.lang.reflect.*;  
class StringReflectDemo  
{  
    public static void main(String[] args) throws Exception  
    {  
        //利用反射获取指定字符串指定位置的字符;  
        String str = "badRomance";  
        //根据方法名和参数获取具体的指定方法;  
        Method charAt = Class.forName("java.lang.String").getMethod("charAt", Integer.TYPE);  
        //在调用时要指定调用该方法的具体对象;  
        char c = (char)charAt.invoke(str,3);  
        System.out.println(c);  
    }  
}  
 
 
(4).使用反射的方式调用某个类的main()方法;
使用反射:在设计源程序时,并不知道使用者传入的类型是什么,但是虽然传入的类名不知道,但是知道这个类中有main方法,所以可以通过反射的方式,通过使用者传入类名,内部通过传入的类名获取main方法,然后执行相应的内容;
在调用main方法时,如何为invoke传递参数,main方法的参数类型应该是一个字符串类型的数组;
但是按照JDK1.4的语法会将数组中的每个字符串作为参数传递给invoke方法,按照JDK1.5的语法会将这整个数组作为参数传递,我们需要的是这种处理方法,可是虚拟机为了兼容之前的语法规则,不幸的是使用JDK1.4的解析方式,而使用这种方式会出现参数类型不正确的情况;
解决方式:编译器不会数组中的元素解析为实际参数;
1).invoke(null, new Object[]{new String[]{"xxxx"}})
2).invoke(null, (Object) new String[]{"xxx"})
示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.zr.day28;  
import java.lang.reflect.*;  
class MainReflectDemo  
{  
    public static void main(String[] args)throws Exception   
    {  
        //使用普通的方式调用  
        Test.main(new String[]{"123","456","789"});  
          
        //使用反射的方式,将需要执行的类通过字符串参数指定;  
        String className = args[0];  
        Class clazz = Class.forName(className);  
          
        //获取指定的main()方法;  
        Method mainMethod = clazz.getMethod("main", String[].class);  
        //方式一:将数组打包,编译器拆包后就是一个String类型的数组;  
        mainMethod.invoke(null,new Object[]{new String[]{"789","456","123"}});  
        //方式二:强制转换为Object,不用拆包  
        mainMethod.invoke(null,(Object)new String[]{"789a","456b","123c"});  
    }  
}  
  
//定义一个测试类,使用反射的方式调用这个类的main方法  
class Test  
{  
    public static void main(String[] args)  
    {  
        for(String str : args)  
        {  
            System.out.println(str);  
        }  
    }  
}  
 
 
3.6 变量的反射:Field
Field类用来代表某个类中的成员变量;
(1).获取Field对象:
Field getField(String s):获取公有的和父类中公有的;
Field getDeclared(String s):获取该类中的任意成员变量,包括私有的;
(2).Field类中常用的方法;
Object get(Object obj):获取指定对象上此Field表示的字段的值;
String  getName():获取此Field对象表示的字段的名称;
void set(Object obj, Object value):将指定对象变量上此Field对象所表示的字段设置为指定的value值;
void setAccessible(boolean flag):如果是私有字段,要先将该私有字段进行取消权限检查的能力,也称之为暴力反射;
示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.zr.day28;  
import java.lang.reflect.*;  
//定义一个用来测试字段反射的类  
class ReflectPoint  
{  
    private int x;  
    private int y;  
    ReflectPoint(int x, int y)  
    {  
        this.x = x;  
        this.y = y;  
    }  
    public String toString(){  
        return "x="+this.x+"**y="+this.y;  
    }  
}  
class FieldReflectDemo  
{  
    public static void main(String[] args)throws Exception  
    {  
        //新建对象  
        ReflectPoint rp = new ReflectPoint(4,5);  
        //获取Class对象实例  
        Class clazz = rp.getClass();  
        //获取该测试类中的所有字段,包括私有  
        Field[] fields = clazz.getDeclaredFields();  
        //遍历所有字段  
        for(Field field : fields)  
        {  
            System.out.println(field.getName());  
        }  
        //获取某个对象的指定字段值  
        //私有获取  
        Field xField = clazz.getDeclaredField("x");  
        //设置访问权限  
        xField.setAccessible(true);  
        //获取字段值  
        System.out.println(xField.get(rp));  
          
        //设置某个对象的字段值  
        Field yField = clazz.getDeclaredField("y");  
        yField.setAccessible(true);  
        //设置字段值  
        yField.set(rp, 99);  
        System.out.println(rp);  
    }  
}  
 
 
3.7 数组的反射
(1).具有相同维数的元素类型的数组属于同一类型,即具有相同的Class实例对象;
(2).Object[]和String[]没有继承关系,Object和String具有继承关系,所以new Object[]{"a","b"}不能强制转换为String[]{"a","b"};
(3).无法得到某个数组的具体类型,只能得到其中某个元素的类型;
(4).基本数据类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;
(5).非基本数据类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用;
(6).Array工具类用于完成对数组的反射操作;Array中提供了大量的用来操作数组的方法;
示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//使用Arrays工具类实现数组的反射,具体实现打印任意数据;  
//如果是数组就打印数组中的元素,否则打印数据本身;  
  
package com.zr.day28;  
  
import java.lang.reflect.Array;  
import java.util.Date;  
  
class ArrayReflectDemo  
{  
    public static void main(String[] args)  
    {  
        //打印数组  
        printObj(new String[]{"dfds","sf","12515","3135","sdrer45d54f"});  
        //打印对象  
        printObj(new Date());  
    }  
    public static void printObj(Object obj)  
    {  
        //首先获取Class实例对象  
        Class clazz = obj.getClass();  
        //判断是否是数组类型  
        if(clazz.isArray())  
        {  
            //使用的是java.lang.reflect.Array中的方法来实现数组的反射;  
            //打印数组中的元素  
            int len = Array.getLength(obj);  
            for(int i=0; i<len; i++)  
            {  
                System.out.println(Array.get(obj, i));  
            }  
        }  
        //打印对象  
        else  
        {  
            System.out.println(obj);  
        }  
    }  
}  
 
 
3.8 内存泄漏
当一个对象存储进HashSet集合中后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了;
在这种情况下,调用Contains方法或者remove方法来查找或者删除这个对象的引用,就会找不到这个对象,从而导致无法从HashSet集合中单独删除当前对象,从而造成内存泄漏;
内存泄漏:某些对象不再使用,占用内存空间,并未释放;
4.内省
 
内省:IntroSpector,主要针对JavaBean进行操作;
4.1 JavaBean概述:
(1).Javabean 是一种特殊的Java类,主要用于传递数据信息,这种Java类中的方法主要用于访问私有的字段,且方法符合某种特殊的命名规则;
(2).只要某个类中的方法有时get或者set开头,就可以当作JavaBean来处理;
(3).一个类被当作JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到Java类内部的成员变量;
4.2 JavaBean的好处:
一个符合JavaBean特点的类可以当作普通类来使用,也可以当作JavaBean来使用,这样会有一些额外的好处;
(1).在JavaEE开发中,经常要使用javaBean,很多环境就要求按JavaBean的方式进行操作;
(2).JDK中提供了对javaBean进行操作的API,这套API称为内省,操作JavaBean比使用普通的方式更方便;
java.beans下定义用于内省的类,其中主要的有PropertyDescriptor类,IntroSpector类和BeanInfo接口;
4.3 简单的内省操作:
根据提供的属性名称,使用PropertyDescriptor来操作:
(1),构造方法:
PropertyDescriptor(String propertyName, class beanClass):参数是属性名称,和满足JavaBean的Class实例;
(2).常用方法:
Method getReadMethod():获得应用于读取属性值的方法;相当于get操作;
Method getWriteMethod():获得应用于写入属性值的方法;相当于set操作;
(3).示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//简单的内省操作  
package com.zr.day28;  
import java.beans.*;  
import java.lang.reflect.*;  
class BeanTest  
{  
    private int x;  
    private int y;  
    BeanTest(int x, int y)  
    {  
        this.x = x;  
        this.y = y;  
    }  
    public String toString()  
    {  
        return "x = "+x+"*** y = "+y;  
    }  
    public int getX() {  
        return x;  
    }  
    public void setX(int x) {  
        this.x = x;  
    }  
    public int getY() {  
        return y;  
    }  
    public void setY(int y) {  
        this.y = y;  
    }  
}  
class JavaBeanDemo  
{  
    public static void main(String[] args)throws Exception  
    {  
        String propertyName = "x";  
        BeanTest bt = new BeanTest(8,9);  
        //使用Eclipse的重构功能,抽取方法,将功能封装成方法;  
        //使用内省的方式  
        //PropertyDescriptor类来操作;  
        PropertyDescriptor pd = new PropertyDescriptor(propertyName,bt.getClass());  
        //首先是调用get方法,即读方法;  
        Method getXMethod = pd.getReadMethod();  
        System.out.println(getXMethod.invoke(bt));  
          
        //接下来是调用set方法,即写方法;  
        int value = 123;  
        Method setXMethod = pd.getWriteMethod();  
        setXMethod.invoke(bt,value);  
        System.out.println(bt);  
    }  
}  
 
 
4.4 复杂的内省操作:
(1).java.beans.IntroSpector:提供的 static BeanInfo getBeanInfo(Class beanClass),对JavaBean进行内省;
(2).java.beans.BeanInfo:提供的PropertyDescriptor[] getPropertyDescriptors(),获取属性;
(3).示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
//复杂的内省操作  
package com.zr.day28;  
import java.beans.*;  
import java.lang.reflect.*;  
class BeanTest  
{  
    private int x;  
    private int y;  
    BeanTest(int x, int y)  
    {  
        this.x = x;  
        this.y = y;  
    }  
    public String toString()  
    {  
        return "x = "+x+"*** y = "+y;  
    }  
    public int getX() {  
        return x;  
    }  
    public void setX(int x) {  
        this.x = x;  
    }  
    public int getY() {  
        return y;  
    }  
    public void setY(int y) {  
        this.y = y;  
    }  
}  
class JavaBeanDemo  
{  
    public static void main(String[] args) throws Exception   
    {  
        String propertyName = "x";  
        BeanTest bt = new BeanTest(8,9);  
        //复杂的内省操作;  
        //调用IntroSpector类中的静态方法,返回BeanInfo信息;  
        BeanInfo bi = Introspector.getBeanInfo(bt.getClass());  
        //获取属性描述  
        PropertyDescriptor[] pds = bi.getPropertyDescriptors();  
        //遍历属性描述信息  
        for(PropertyDescriptor pd : pds )  
        {  
            //判断属性名陈是否是指定的属性  
            if(pd.getName().equals(propertyName))  
            {  
                //获取需要的方法  
                Method getXMethod = pd.getReadMethod();  
                System.out.println(getXMethod.invoke(bt));  
                break;  
            }  
        }  
    }  
}  
 
 
4.5 BeanUtils工具包
(1).该工具包中的set和get方法中,传入的是字符串,返回的也是字符串,因为在浏览器中,用户输入到文本框的都是以字符串的形式发送到服务器上的,所以操作的都是字符串,也就是说这个工具包的内部有自动将其他类型数据转换成字符串的操作;
(2).支持属性的级联操作;
5.注解
 
5.1 注解概述
注解Annotation,相当于一种标记,在程序中加入注解就等于为程序打上某种标记;
以后Java编译器,开发工具,和其他程序就可以通过反射获取该类的各个成分上有无标记,或者是有什么标记,然后做出相应动作;
注解可以加在包,类,字段,方法,方法的参数以及局部变量上;
在java.lang包中提供了最基本的注解;
注解的使用格式:@注解名
(1).如果只有一个value名称的属性或其他属性缺省,则@注解名(“属性值”);
(2).如果有多个或不缺省的或者是需要重新赋值的属性,则使用@注解名(属性名=“属性值”,...);
Class类中和注解有关的方法:
Annotation[] getAnnotations():返回此元素上存在的所有注释;
Object getAnnotation(class annotationClass):如果存在指定的注解,则返回这些注解,否则返回null;
boolean isAnnotation():如果此Class对象表示一个注释类型则返回true;
boolean isAnnotationPresent(Class annotationClass):判断该Class对象上是否有指定类型的注解;
5.2 基本注解
(1).public @interface SuppressWarnings :告知编译器或开发工具等不再提示指定的警告;
@Retention(value = SOURCE)
(2).public @interface Deprecated :告知调用者,该方法或者字段已经过时,不推荐使用;
@Retention(value = RUNTIME)
(3).public @interface Override :表示接下来的方式是覆盖的父类中的方法,如果不存在覆盖关系,就会报错;
@Target(value = METHOD)
@Retention(value = SOURCE)
5.3 注解的应用
注解就相当于一个源程序中要使用的一个类,要在源程序中应用某个注解,就必须先定义好这个注解类;
注解的应用结构图:
 
自定义注解:
(1).格式:@interface 注解名称{ statement }
(2).元注解:注解的注解
两个常用的元注解:
注解一:Retention
用于说明注解保留在那个阶段,定义了注解的生命周期;
一个注解的生命周期:Java源程序--(javac)-->class文件--(类加载器)-->内存中的字节码;
class文件中不是字节码,只有class文件中的内容加载进内存,用类加载器加载处理后(进行完整的检查处理后),最后得到的二进制内容才是字节码;
分别对应Retention这个枚举类的值:
1).RetentionPolicy.SOURCE:Java源文件时期,如@Override和@SuppressWarnings
2).RetentionPolicy.CLASS:class文件时期,默认阶段
3).RetentionPolicy.RUNTIME:运行时期,如@Deprected
注解二:Target
用于说明注解类的使用范围,默认值是任何地方;
Target的取值可以是枚举类ElementType中的任意一个:
PACKAGE(包声明)
FIELD(字段声明)
ANNOTATION_TYPE(注释类型声明)
CONSTRUCTOR(构造器声明)
METHOD(方法声明)
PARAMETER(参数声明)
TYPE:class,enum,interface,@interface
LOCAL_VARIABLE(局部变量声明)
(3).自定义注解示例
第一:定义注解类,
@interface A{  }
第二:定义应用了”注释类“的类,
@A
class B{  }
第三:对应用来注解类的类进行反射操作的类,
class C 
{
B.class.isAnnotationPresent(A.class);//判断是否该类应用了指定的注解类;
A a = B.class.getAnnotation(A.class);//获取这个注释类的对象;
}
5.4 为注解添加基本属性
(1).定义格式,相当于接口中的方法;
eg:String color();
eg:String value() default "zr";//定义缺省值;
(2).基本属性的应用:直接在注解的括号中添加自身的属性;
eg:@MyAnnotation(color="red")
说明:
1).如果注解中有一个名称为value的属性,其他属性值都采用缺省方式定义,或者只有value属性,那么可以省略value=部分;
2).可以为属性值指定缺省值,在应用时可以重新设置属性值;
3).用反射的方式获得注解对应的实例对象后,可以通过该对象调用属性对应的方法来获取属性值;
5.5 为注解添加高级属性
可以对注解增加的高级属性有:
(1).八种基本数据类型;
(2).String类型;
(3).Class类型;
(4).枚举类型;
(5).注解类型;
(6).前五种类型的数组;
注解示例:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.zr.day28;    
    
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.METHOD,ElementType.TYPE})//元注解,指定使用范围    
//注解类    
public @interface MyAnnotation {    
    String color() default "red" ;    
    String value();    
    //数组    
    int[] arr() default {1,2,3};    
    //枚举    
    EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.GREEN;    
    //注解类    
    MetaAnnotation annotation() default @MetaAnnotation("heima");    
    //Class类    
    Class clazz() default System.class;    
}    
    
import java.lang.reflect.Method;    
    
//注解类的应用,给属性赋值或者重新赋值    
@MyAnnotation(lamp=EnumTest.TrafficLamp.YELLOW,value="heima",    
            clazz=AnnotationDemo.class,annotation=@MetaAnnotation("itheima"))    
//应用类    
public class AnnotationDemo {    
    @SuppressWarnings("deprecation")//此注解用于抑制过时信息的提示    
    @MyAnnotation("Method")//自定义注解应用在方法上    
    public static void main(String[] args) throws NoSuchMethodException, SecurityException {    
            
        System.runFinalizersOnExit(true); //这是一个过时了的方法 ,如果没有注解就会有警告提示    
        //判断此类是否有MyAnnotation注解    
        if (AnnotationDemo.class.isAnnotationPresent(MyAnnotation.class)) {    
            //如果有,则获取该注解    
            MyAnnotation annotation =AnnotationDemo.class.getAnnotation(MyAnnotation.c