黑马程序员---java基础-Java之反射

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、反射作用

u 1、为什么要用反射

Java程序中的许多对象在运行时都会出现两种类型:编译时的类型和运行时的类型,如Person p=new Student(); 这行代码将会生成一个p的变量,该变量编译时类型是Person,运行时的类型是Student;除此之外,还有更极端的情形,程序在运行时接受到外部传入的一个对象,该对象的编译时的类型是Object,但是程序又需要调用该对象运行时类型的方法。

为了解决这些问题,程序需要在运行时发现对象和类的真实信息。为了解决这个问题,我们有一下两种做法:

1、假设在编译时和运行时都完全知道类型的具体信息,在这种情况下,我们可以直接先使用instanceof运算符进行判断,再利用强制类型转换将其转换成其运行时类型的变量即可。

2、编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。

u 2、什么是反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

反射就是把Java类中的各种成分,映射成相应的Java类,例如一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个的Java类来表示,就想汽车是一个类,汽车中的发动机,变速箱等等也是一个个类。表示Java类的Class类显然要提供一系列的方法,来获得其中的变量,方法、构造函数、修饰符、包等信息,这些信息就是用相应类的实例对象来表示,他们是Field/Method/Constructor/Package等等

u 3、反射作用

在运行时判断任意一个对象所属的类;

在运行时构造任意一个类的对象;

在运行时判断任意一个类所具有的成员变量和方法;

在运行时调用任意一个对象的方法;

生成动态代理。

序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields(成员属性)设值、或唤起其methods成员方法。

反射可以程序运行时,改变程序结构或变量类型,比如为一个Integer集合添加String元素。也可以改变或获取成员属性(字段),获取构造方法,获取并调用成员方法。

1、获得Class对象(即返回字节码的方法)

--->System.class、 Person.getClass()、Class.forName("java.util.data")

大家都知道,每个类被加载之后,系统就会对该类生成一个对应的class对象,通过该class 对象就可以访问到JVM中的这个类。

在Java程序中获得Class对象通常有如下3中方式:

·  使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数, 该字符串参数的值是某个类的全限定类名(必须添加完整包名)

String className ="cn.itcast.bean.Person";

//这里要注意:是包名.类名的字符串表现形式,这与导不导包没有关系

Class clazz = Class.forName(className);

·  调用某个类的class属性来获取该类对应的Class对象。例如Person.class将会返回 Person类对应的class对象。

相对简单,但是还是要明确用到类中的静态成员,缺点:不够扩展

public static void getClassObject_2() {

Class clazz = Person.class;

· 调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法,所以 所有的Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class 对象。想要用这种方式,必须要明确具体的类,并创建对象。缺点是不够简单:

1 String className ="cn.itcast.bean.Person";
2
3 //这里要注意:是包名.类名的字符串表现形式,这与导不导包没有关系
4
5 Class clazz = Class.forName(className);

对于第一种和第二种方式都是直接根据类来获取该类的class对象,相比之下,第二种方式有如下两种优势

·代码更安全。程序在编译阶段就可以检查需要访问的Class对象是否存在

·程序性能更好。因为这种方式无需调用方法,所以性能更好

大部分时候我们都应该使用第二种方式来获取指定类的Class对象。但是如果我们只有一个字符串,如“java.lang.String”,若需要获取该字符串对应的Class对象,则只能使用第一种方法,使用Class 的forName(String clazzName)方法来获取Class对象时,该方法可能会抛出一个ClassNotFoundException异常。所以第一种和第二种是最常用的方法。

一旦获得了某个类所对应的Class对象之后,程序就可以调用Class对象的方法来获得该对象和该类的真实信息了。

 1 import java.lang.reflect.Array;
 2 import java.lang.reflect.Constructor;
 3 import java.lang.reflect.Field;
 4 import java.lang.reflect.Method;
 5 import java.util.Arrays;
 6
 7 public class ReflectText {
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args)throws Exception {
12         String str1 =  "abc";
13         Class cls1 = str1.getClass();
14         Class cls2 = String.class;
15         Class cls3 = Class.forName("java.lang.String");
16         System.out.println(cls1==cls2);true
17         System.out.println(cls1==cls2);true
18
19         System.out.println(cls1.isPrimitive());//cls1是否是基本类型字节码  String非基本类型
20         System.out.println(int.class.isPrimitive());//基本类型
21         System.out.println(int.class==Integer.class);//Integer类型的字节码
22         System.out.println(int.class==Integer.TYPE);//是否和包中的基本类型的字节码相同
23         System.out.println(int[].class.isPrimitive());//是一个数组 ,但是不是原始类型  数组也是一种类型
24         System.out.println(int[].class.isArray());//是否是个数组呢
25 }
26 }

总之只要是在源程序中出现的类型都有各自的Class实例对象,例如int[] void都有各自的Class实例对象,可以调用isArray() 或者isPrimitive()等方法!

二、反射应用

u 1、Constructor类---->构造方法的反射应用

得到了构造方法,我们就可以得到自己所属的类getDeclaringClass(),得到前边的修饰符getModifiors(),最重要的是构造一个实例对象newInstance()而我们new 一个构造方法的时候一定要传一个相应的对象进去

Constuctor constructor1=String.class.getConstructor(StringBuffer.class);//StringBuffer表示选择那个构造方法

String str1=(String)constructor1.newInstance(StringBuffer("abc"));//表示用这个构造方法的时候还得传一个StringBuffer的对象进去,两个必须一致

编译的时候是机器语言11000111之类的,并不知道这个构造方法的返回值是String,所以得加上(String)的强制类型转换,只有在运行的时候才知道。

Constructor constructor1=String.class.getConstructor(StringBuffer.class); //获得方法时

String str2=(String)constructor1.newInstance(new StringBuffer("abc"));//调用获得的方法时

//输出str2的第三个字符

System.out.println(str2.charAt(2));

过程:得到构造方法,再用构造方法newInstance()这就是反射

·得到某个类所有的构造方法:

Constructor[] Constructor=Class.forName("java.lang.String").getConstructors();

·得到某一个构造方法 :Constructor

Constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);

(根据接受的参数类型获得制定的构造方法,参数类型是StringBuffer,里面就加上StringBuffer.class)

里面也可以写多个参数类型的class对象

Class.newInstance()方法 :

·例子:String Obj=(String)class.forName("java.lang.String").newInstance();

·该例子内部先得到默认的构造方法,然后用该构造方法创建实例对象

·该方法内部的具体代码是用到了缓存机制来保存默认构造方法的实例对象。

u 2、Filed类----->成员变量的反射应用

 1 import java.lang.reflect.Field;
 2
 3 public     class ReflectText{
 4     public static void main(String[] args) throws Exception{
 5         ReflectPoint pt1=new ReflectPoint(3,5);
 6         Field fieldY=pt1.getClass().getField("y");
 7         Field fieldX=pt1.getClass().getDeclaredField("x");
 8         fieldX.setAccessible(true);
 9         System.out.println(fieldY.get(pt1));
10         System.out.println(fieldX.get(pt1));
11         changeString(pt1);
12         System.out.println(pt1);
13     }
14     static void changeString(Object obj) throws Exception{
15         //得到所有的字段
16         Field[] fields=obj.getClass().getFields();
17         //对字段进行迭代
18         for(Field field : fields){
19 //            if(field.getType().equals(String.class)){}
20             //只要是比较字节码的比较应该用“==”比较,因为是同一份字节码
21             //意思是只要是String类型的就要取得他的值,然后进行替换
22             if(field.getType()==(String.class)){
23                 String oldValue=(String)field.get(obj);
24                 //将取得的字符串其中的"b"改成"a",调用String的方法replace()
25                 String newValue=oldValue.replace(‘b‘, ‘a‘);
26                 field.set(obj,newValue);
27             }
28             }
29         }
30 }
31 class ReflectPoint {
32     private int x;
33     public int y;
34     @Override
35     public String toString() {
36         return "ReflectPoint [str1=" + str1 + ", str2=" + str2 + ", str3="
37                 + str3 + "]";
38     }
39     public String str1="ball";
40     public String str2="basketball";
41     public String str3="acdefg";
42     //alt+shift+s为x和y生成构造方法
43     public ReflectPoint(int x, int y) {
44         super();
45         this.x = x;
46         this.y = y;
47     }
48 }

上边的changeString()方法应牢记,懂得了这个方法,就能掌握反射了!

u 3、Method类----->成员方法的反射应用

普通的调用方法:

 1 class ReflectText5
 2 {
 3     public static void main(String[] args)
 4     {
 5         //用普通方法调用
 6     TestArguments.main(new String[]{"111","222","333"});
 7     }
 8 }
 9 class TestArguments
10 {
11     public static void main(String[] args)
12     {
13         for(String arg : args){
14         System.out.println(arg);
15     }
16 }

反射调用:

 1 import java.lang.reflect.Method;
 2 class ReflectText5 {
 3     public static void main(String[] args) throws Exception {
 4         // 用普通方法调用
 5         // TestArguments.main(new String[]{"111","222","333"});
 6         // 用反射方法调用
 7         String startingClassName = args[0];
 8         Method mainMethod = Class.forName(startingClassName).getMethod("main",
 9                 String[].class);
10         // main是静态的,调用静态的方法不用传递参数,所以是null
11         // 下面两种方法都可以
12         // mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
13         mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
14     }
15 }
16
17 class TestArguments {
18     public static void main(String[] args) {
19         for (String arg : args) {
20             System.out.println(arg);
21         }
22     }
23 }

u 4、数组的反射

具有相同维数和元素类型的数组属于同一类型,即具有相同的Class实例对象。

代表数组的Class实例对象的getSuperClass方法返回的父类为Object类对应的Class.

基本类型的一维数组可以被当做Object类型使用,不能当作Object[]类型使用,非基本类型(int)的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用

Array.asList()方法处理int[],String[]的区别

Array工具类用于完成对数组的反射操作

 1 import java.util.ArrayList;
 2 import java.util.Collection;
 3 import java.util.HashMap;
 4 import java.util.HashSet;
 5 public class ReflectText8 {
 6     public static void main(String[] args) {
 7         // Collection collections=new ArrayList();
 8         //打印出来是4
 9         Collection collections=new HashSet();
10         //打印出来是3
11         ReflectPoint pt1=new ReflectPoint(3,3);
12         ReflectPoint pt2=new ReflectPoint(5,5);
13         ReflectPoint pt3=new ReflectPoint(3,3);
14         collections.add(pt1);
15         collections.add(pt2);
16         collections.add(pt3);
17         collections.add(pt1);
18         System.out.println(collections.size());
19     }
20 }
时间: 2024-08-28 18:33:23

黑马程序员---java基础-Java之反射的相关文章

黑马程序员   两道java面试题

1.List,Set,Map是否都继承了Collection接口?  List,Map比较常用:List是通过下标来访问元素,Map是通过键值进行访问元素.应该不会同时都实现了Collection接口.  Collection是一个什么概念?还有Set在java中是怎样定义的?他是通过下标来访问元素还是通过键值来访问元素?两者谁继承了Collection接口呢?或者有没有可能两者都继承了Collection接口?时间不多了,我是这样写的:  Set,Map实现了Collection接口.而Lis

黑马程序员 【】java学习之路——TCP(三)客户端上传文件到服务器

------- <a href="http://www.itheima.com" target="blank">android培训</a>.<a href="http://www.itheima.com" target="blank">java培训</a>.期待与您交流! ---------- import java.io.*; import java.net.*; class

黑马程序员_Java异常 Java常用库类

Java异常 1,异常的基本概念 异常是导致程序中断运行的一种指令流,它是在运行时期发生的不正常的情况,在Java中,一切的异常都秉着面向对象的设计思想,所有的异常都是以对象和类的形式存在的. 2,异常类的继承结构 在整个Java的异常结构中,实际上有两个最常用的类,Exception和Error,这两个类分别是Throwable的子类 Exception:一般表示的是程序中出现的问题,可以直接使用try....catch处理 Error:一般之的是JVM的错误,程序中无法处理. 3,Java的

黑马程序员_JAVA 基础加强学习笔记

一.面向对象 (一)继承  1.继承的好处: (1) 提高了代码的复用性. (2) 让类与类之间产生了关系,提供了另一个特征多态的前提. 注意: 子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super();  如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数. 如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数. 2.final特点

黑马程序员_Java基础加强(下)

8.注解类 注解相当于一种标记,加了注解就等于打上了某种标记,没加就等于没打特殊标记,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记就去干什么事,标记可以加在包.类.字段.方法,方法的参数以及局部变量上. 注解的应用结构图: 为注解增加属性 定义基本类型的属性和应用属性 在注解类中增加String color(); @MyAnnotation(color = "red") 用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法 MyAnno

黑马程序员_Java基础加强(上)

1.静态导入 静态导入是jdk1.5版本以后出现的新特性,一般是指导入静态方法,如:import static java.lang.System.out 是指导入系统输出的静态方法. 例: import static java.lang.System.out //导入java.lang包下的System类的静态方法out public class StaticImport { public static void main(String[] args) { int x=1; x++; out.p

黑马程序员-正则表达式基础

正则表达式是一种描述字符串集的方法,它是以字符串集中各字符串的共有特征为依据的.正则表达式可以用于搜索.编辑或者是操作文本和数据.它超出了 Java 程序设计语言的标准语法,因此有必要去学习特定的语法来构建正则表达式.正则表达式的变化是复杂的,一旦你理解了它们是如何被构造的话,你就能解析或者构建任意的正则表达式了..正则表达式由一些普通字符和一些元字符组成. 普通字符包括大小写的字母和数字,而元字符则具有特殊的含义.在最简单的情况下,一个正则表达式看上去就是一个普通的查找串.例如,正则表达式"a

黑马程序员_Java基础_接口

------- android培训.java培训.期待与您交流! ---------- 0.接口知识体系 Java接口的知识体系如下图所示,掌握下图中的所有知识就可精通接口. 1.接口概论 1)接口概念 接口是从多个相似类中抽象出来的规范,接口中不包含普通方法,所有方法都是抽象方法,接口不提供实现.接口体现的是规范和实现分离的哲学.规范和实现分离正是接口的好处,让软件系统的各个组件之间面向接口耦合,是一种松耦合的设计.接口定义的是多个类共同的公共行为规范,定义的是一组公用方法. 2)接口与抽象类

黑马程序员——集合基础知识(Map)

Map概念 要同时存储两个元素,他们之间有映射关系,每个键不能重复,每个键只能映射到一个值. 存储键值对,并且键是唯一的. 1.添加. put()如果添加的键原来有值,后添加的值会覆盖前面的值,并返回之前的值. 2.删除 remove()按键删除. 3.判断 4.获取 get(object key) size() value()拿值value返回的是值的集合... HashTable 底层是哈西数据结构,不可以存入null键null值,线程同步. HashMap 底层是哈西表数据结构,允许使用n

黑马程序员_Java基础String类

- - - - - android培训.java培训.期待与您交流! - - - - - - - String是一个对象 String不属于8种基本数据类型(byte, char, short, int, float, long, double, boolean),String是对象,所以其默认值是null. String是一种特殊的对象,有其它对象没有的一些特性,通过JDK发现: public final class String implements java.io.Serializable