您的位置:首页 > 职场人生

黑马程序员————java中类的加载、反射、动态代理、枚举

2015-06-19 22:23 711 查看


------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

类的加载、反射、动态代理、枚举

一.类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

1.加载:

就是指将class文件读入内存,并为之创建一个Class对象

★★ 任何类被使用时系统都会建立一个Class对象

2.连接:

验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用

3.初始化:

先是静态初始化,后是成员变量初始化

4.类的初始化时机

创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类

5.类的加载器:

负责将.class文件加载到内存中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
类加载器的组成:
Bootstrap ClassLoader 根类加载器(核心类库)
Extension ClassLoader 扩展类加载器(工具插件类)
Sysetm ClassLoader 系统类加载器(我们自定义类)
类加载器的作用:
Bootstrap ClassLoader 根类加载器:也被称为引导类加载器,负责Java核心类的加载,比如System,String等。
在JDK中JRE的lib目录下rt.jar文件中。
Extension ClassLoader 扩展类加载器:负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。
System ClassLoader 系统类加载器:负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

二.反射

1.反射的概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。
而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

2.获取的Class对象的三种方式:

1).使用对象,调用getClass();

它是Object类中的方法。此方法是普通方法,不是静态的,要通过类的对象的引用去调用。
Student stu = new Studetn();
Class stuClass = stu.getClass();

2).静态的class属性;

任何的"数据类型"都有一个"静态的属性"--class属性,这个属性可以获取这个类的Class对象
int.class; 
Class stuClass2 = Student.class;

3).Class类forName()方法;

用Class类中的静态方法forName()可以获取某个类的Class对象;
public static Class forName(String className)
这里的classname是一个全名限定的类名,也就是带包名的。
Class stuClass3 = Class.forName("cn.itcast.demo01_获取Class对象的三种方式.Student");

   注意:在运行期间,一个类,只有一个Class对象产生。也就是说无论哪种方式获取的class对象都是同一个对象。

三:通过反射获取和使用构造方法和类中成员变量即成员方法

1.获取构造方法:使用的都是class类中的方法

批量获取:
public Constructor<?>[] getConstructors():获取所有公有的构造方法;返回一个包含某些 Constructor 对象引用的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法
范例 for(Constructor c : conArray ){

★★ System.out.println(c);}//打印出来的就是构造方法名称,如:public cn.itcast.demo02_通过Class获取某个类的的构造方法并调用.Student(boolean)

public Constructor<?>[] getDeclaredConstructors():获取所有的构造方法。包括公有,受保护,默认,私有的构造方法。

获取单个:
public Constructor<T> getConstructor(Class<?>... parameterTypes):获取的有参数的公有的构造方法。
parameterTypes 构造方法形参的类型的Class对象。
范例:Constructor con2 = stuClass.getConstructor(boolean.class);
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定的构造方法。可以是私有的。

2.调用构造方法:使用Constructor中的方法

public T newInstance(Object... initargs):使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例。
谁调用此方法就使用那个构造方法创建一个对象。也就是返回的是一个类的对象,而所传的参数就是调用构造方法所用的实际参数。
范例:obj = con2.newInstance(true);
System.out.println("obj = " + obj);//obj = cn.itcast.demo02_通过Class获取某个类的的构造方法并调用.Student@1f82982

//由于要访问是私有成员,所以这里要设置"取消访问限制检查",进行暴力访问
public void setAccessible(boolean flag),Constructor的父类AccessibleObject中的方法,取消访问检查。
可以用来访问类中的私有成员

    3.获取成员属性:class类中的方法

批量获取:
public Field[] getFields():获取所有公有的成员变量
public Field[] getDeclaredFields():获取所有的属性。包括私有的
获取单个:
public Field getField(String name):获取公有的指定属性
范例:Field f = stuClass.getField("stuName");

★★ System.out.println(f);//打印的是是属性名,而不是值。★★
public Field getDeclaredField(String name):获取指定属性。可以是私有的。

4.获取、设置属性值:Field类中的方法

public void set(Object obj,Object value):注意:设置前,一定要先实例化一个此类的对象。
参数:obj - 应该修改其字段的对象,也就是此字段所在类的对象;value - 正被修改的 obj 的字段的新值。
public object get(Object obj):获取字段的值,参数就是字段所在的类的对像,从中提取所表示字段的值的对象 。

范例:
//先实例化出一个Student对象
Object stuObj = stuClass.getConstructor().newInstance();
//为字段设置值
f.set(stuObj, "张学友");
//获取字段的值:
System.out.println("获取字段的值:" + f.get(stuObj));

对于私有的属性,要经行包里访问。
public void setAccessible(boolean flag),
Field类对象.setAccessible(true);

5.获取成员方法:class类中的方法

批量获取:
public Method[] getMethods() :获取所有的"公有"方法.并且从父类继承的方法也有★★★
public Method[] getDeclaredMethods() :获取所有方法,包括私有的。
范例:
for(Method m : methodArray){
System.out.println(m);打印的是方法名称(带参数类型)show(int)★★
获取单个:
public Method getMethod(String name, Class<?>... parameterTypes) :获取某个公有的方法。

                形参:name:方法名; parameterTypes:形参的类型列表,可变参数
public Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取某个方法。可以是私有的。

6.访问成员方法:Method类中方法

注意:调用方法前,一定要先实例化一个此类对象
public Object invoke(Object obj,Object... args);object就是使用args实参后方法的返回值,★★
参数:obj : 方法所属的对象;args: 调用方法需要的实参

同样暴力访问
public void setAccessible(boolean flag)
method对象.setAccessible(true)

四.反射的重要应用:

1.通过反射运行配置文件的内容

public class Demo {
public static void main(String[] args) throws Exception{

/*
//获取一个class类对象,使用三种方法中那一中,class类中静态方法
Class aclass =Class.forName("cn.itcast.Test反射读取配置文件.line2");
//由获得Class对象再得到一个该类的一个对象
Object obj=aclass.getConstructor().newInstance();
//得到的该类的一个成员方法
Method m=aclass.getMethod("move", int.class);
m.invoke(obj, 20);
*/

//定义一个class类对象
Class aclass=Class.forName(getvalue("classname"));
//通过反射,并调用构造方法,创建一个该类的对象
Object obj=aclass.getConstructor(String.class).newInstance("好方法");
//通过反射调用其成员方法
aclass.getMethod(getvalue("methodname"), int.class).invoke(obj, 50);
}
//通过定义一个方法来获取几种东西
public static String getvalue(String str)throws IOException{
//定义一个properties集合map集合,以便于读写文件
Properties pro=new Properties();
//定义一个输入流
FileReader filein=new FileReader(new File("pro.properties"));
//读取文件中内容到集合中
pro.load(filein);
//关闭资源
filein.close();
return pro.getProperty(str);
}
}
public class line2 {
private String name;
public line2(String name){
this.name =name;
}
public void move(int n){
System.out.println("名字是:"+name+"小伙的年龄是"+n);
}
}

2.通过泛型越过泛型检查

泛型只是在编译期间有作用,可以通过泛型直接加载其class,往声明泛型的集合添加东西

public class Demo {
public static void main(String[] args) throws Exception  {
//定义一个集合
ArrayList<Integer> list=new ArrayList<>();
list.add(10);
//使用反射创建此类的class对象,调用其方法
Class aclass=ArrayList.class;//list.getclass();
//获取其方法的对象,使用的参数为object.class,通用的类型
Method m=aclass.getMethod("add", Object.class);
m.invoke(list, "nihaoma ");
System.out.println(list);
}
}

3.通过泛型设置某个对象的某个属性为指定值

public class Demo {
public static void main(String[] args) throws Exception {
Cat c=new Cat();
setValue(c,"name","bosimao");
setValue(c, "age", 2);
System.out.println("年龄:"+c.getAge()+"名字:"+c.getName());
}

private static void setValue(Object obj, String fieldName, Object value) throws Exception {
Class aclass=obj.getClass();
Field field=aclass.getDeclaredField(fieldName);
//由于是私有属性,需要暴力访问
field.setAccessible(true);
field.set( obj, value);
}
}
public class Cat {
private String name;
private int age=0;

public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}

五.动态代理

1.动态代理的概述

本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理

2.代理模式:

1).Student类中有一个coding()方法,写程序;
2).测试类中如果需要coding()方法,需要直接实例化一个Student,并调用方法;
如果想在coding()方法的前面或者后面添加一些其他功能,可以不用修改Student类,而为Student类添加
一个"代理类",代理类会调用Student类中的方法。
3).增加了代理类后,测试类不需要直接面对Student,转而使用代理类。代理类中为coding方法添加了新的功能。
实际上就是不直接使用基础类,而是使用第三方代理,并且代理可以添加一些基本功能
public class Demo {//测试类
pub
b6f7
lic static void main(String[] args) {
/*
Student stu = new Student();
stu.coding();
*/
//使用代理模式
StudentProxy proxy = new StudentProxy();
proxy.coding();
}
}

public class StudentProxy {//代理类
public void coding(){
check();
new Student().coding();
zj();
}
//先期检查
public void check(){
System.out.println("先期检查......");
}
//后期总结
public void zj(){
System.out.println("后期总结.......");
}
}

public class Student {//基础类
public void coding(){
System.out.println("做项目,写程序......");
}
}

3.动态代理的步骤;

1).自定义一个类,实现InvocationHander接口,并重写接口中的invoke()方法
2).在需要使用代理类的时候,使用Proxy类中的newProxyInstance()方法获取代理类的对象
3).JDK只能为接口做动态代理,所以要为所要代理的类定义一个接口,并去实现接口
public class Demo {//测试类
public static void main(String[] args) throws Exception {
//多态的子类对象,使用proxy类中的方法newproxyInstace

//Idao idaostu=(Idao) Proxy.newProxyInstance(Class.forName("cn.itcast.Test动态代理.Student").getClassLoader(),
//									Student.class.getInterfaces(),new MyInvocationhander(new Student()));

Idao idaostu=(Idao) Proxy.newProxyInstance(Student.class.getClassLoader(),
Student.class.getInterfaces(),
new MyInvocationhander(new Student()));
idaostu.write();//需要使用的方法
}
}

public class MyInvocationhander implements InvocationHandler {//实现接口,代理类

private Object obj;//定义需要代理的对象
public MyInvocationhander(Object obj) {
this.obj=obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object objmethod=method.invoke(obj);
show();
return objmethod;
}

private void show() {
System.out.println("真是牛叉啊!!");
}
}

public interface Idao {//自己定义接口
public void write();
}

public class Student implements Idao {//自定义类,需要代理的类

@Override
public void write() {
System.out.println("你好,动态代理!!");
}
}


六.枚举

1.枚举概述

一种模式"多例模式":在整个应用程序运行期间,有些类的实例,只允许有固定的几个,这种模式就是"多例模式"

  例如:骰子:需要2个实例;

          扑克:54个实例;        

    现在说的枚举,就是基于"多例模式":  
例如:我们的程序运行期间,需要三个颜色(红\绿\蓝),所以我们使用一个类MyColor来表示颜色。因为我们只需要三个颜色,所以这个类的对象,只能有三个;

public abstract class MyColor3 {//抽象类也是可以定义多例模式
public static final MyColor3 RED  = new MyColor3("红"){
@Override
void show() {
System.out.println("我是红色的!");
}};
public static final MyColor3 GREEN = new MyColor3("绿"){
@Override
void show() {
System.out.println("我是绿色的!");
}};
public static final MyColor3 BLUE = new MyColor3("蓝"){
@Override
void show() {
System.out.println("我是蓝色的!");
}};
private String colorName;

private MyColor3(String colorName){
this.colorName = colorName;
}

public String toString(){
return this.colorName;
}

abstract void show();
}

2.制作枚举步骤:

1).定义枚举:public enum Xxxxx
2).直接定义枚举项。注意:可以有其它成员,但枚举项必须在第一行;
3).枚举中可以包含抽象方法;
4).任何的枚举类,都继承自:Enum类;
public enum MyColor3 {
RED("红") {
@Override
void show() {
System.out.println("我是红色的!");
}
},GREEN("绿") {
@Override
void show() {
System.out.println("我是绿色的!");
}
},BLUE("蓝") {
@Override
void show() {
System.out.println("我是蓝色的!");
}
};
private String colorName;
private MyColor3(String n){
this.colorName = n;
}

public String getColorName(){
return this.colorName;
}

abstract void show();
}

3.枚举类中常用方法,使用枚举对象调用

int compareTo(E o)//比较的是枚举项的索引值,做减法
String name()//枚举项的名字fieldname
int ordinal()//枚举项的索引值
String toString()//枚举对象所对对应的fieldname
<T> T valueOf(Class<T> type,String name)//将一个字符串转换为type类型的对象
values() 
此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便

public class Demo {
public static void main(String[] args) {
MyColor3 c1 = MyColor3.RED;
MyColor3 c2 = MyColor3.BLUE;

System.out.println(c2.compareTo(c1));//索引值的减法
System.out.println("name = " + c2.name());//BLUE
//int ordinal()
System.out.println("c2.ordinal() : " + c2.ordinal());
System.out.println("c1.ordinal() : " + c1.ordinal());
//String toString()
System.out.println("c2.toString():" + c2.toString());
//<T> T valueOf(Class<T> type,String name)
MyColor3 c3 = c1.valueOf("RED");//将一个字符串转换为MyColor3对象
MyColor3 c4 = MyColor3.valueOf(MyColor3.class,"BLUE");

System.out.println(c3);
System.out.println(c4);

//values()
MyColor3[] result = c1.values();
System.out.println("循环遍历:");
for(MyColor3 c : result){
System.out.println(c);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息