一、反射的引入:
Object obj = new Student();
若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:
1.若编译和运行类型都知道,使用 instanceof判断后,强转。
2.编译时根本无法预知该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,这时就必须使用反射了。
二、反射的概念
就是把java类中的各个成分映射成相应的java类。
反射机制并非JDK1.5的新特性,其从JDK1.2就开始存在;Class类区别于class关键字。(在定义Class类的对象时为了去别于class关键字往往将变量名写成clazz)Class类代表了java程序中的java类。
九个预定义的Class对象:基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void通过class属性也可以表示为 Class 对象;
package com.qba.fanshe;
public class FanSheDemo {
public static void main(String[] args) {
Class clazz1=int.class;
Class clazz2=Integer.class;
System.out.println(clazz1);
System.out.println(clazz2);
System.out.println(clazz1==clazz2);
}
}
输出结果为:
int
class java.lang.Integer
false
Class不能直接new,构造对象的三种方法:
package com.qba.fanshe;
public class FanSheDemo {
public static void main(String[] args) throws Exception {
String s="abc";
Class clazz1=s.getClass();
Class clazz2=String.class;
Class clazz3=Class.forName("java.lang.String");
System.out.println(clazz1);
System.out.println(clazz2);
System.out.println(clazz3);
}
}
输出结果为:
class java.lang.String
class java.lang.String
class java.lang.String
注意:第三种方式会抛出异常
三、利用Class获取类的属性信息
1.获取类名、包名、接口等
package com.qba.fanshe;
import java.lang.reflect.Modifier;
class A {
}
interface B{
}
interface C{
}
public class FanSheDemo extends A implements B,C{
//内部类
public class C{}
public interface D{}
public static void main(String[] args) {
//类可以,接口也可以
Class<FanSheDemo> c = FanSheDemo.class;
System.out.println(c);//class junereflect624.BaseDemo3
//得到包名
System.out.println(c.getPackage());
//得到全限定名
System.out.println(c.getName());
//得到类的简称
System.out.println(c.getSimpleName());
//得到父类
System.out.println(c.getSuperclass().getSimpleName());
//得到接口
System.out.println(c.getInterfaces());
Class[] arr = c.getInterfaces();
for (Class cla : arr) {
System.out.println(cla);
}
//获得public修饰的类
Class[] cl = c.getClasses();
System.out.println(cl.length);//在内部类没有加上public修饰的时候长度为0,加上就是2(获取的是公共的)
for (Class class1 : cl) {
System.out.println(class1);
}
//获得修饰符
int i = c.getModifiers();
System.out.println(i);//常量值1表示public
System.out.println(Modifier.toString(i));//直接打印出public
}
}
输出结果为:class com.qba.fanshe.FanSheDemopackage com.qba.fanshe
com.qba.fanshe.FanSheDemo
FanSheDemo
A
[Ljava.lang.Class;@170bea5
interface com.qba.fanshe.B
interface com.qba.fanshe.C
2
class com.qba.fanshe.FanSheDemo$C
interface com.qba.fanshe.FanSheDemo$D
1
public
2. Class中得到构造方法Constructor、方法Method、字段Field(张孝祥老师视频里代码)
package com.qba.fanshe;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class FansheTest {
public static void main(String[] args) throws Exception {
String str1="abc";
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls3==cls2);
System.out.println(cls1.isPrimitive());//判断是否是基本数据类型
System.out.println(int.class.isPrimitive());
System.out.println(int.class==Integer.class);
System.out.println(int.class==Integer.TYPE);//包装类型所包装的基本类型字节码
System.out.println(int[].class.isArray());//判断是否是数组类型
//参数为StringBuffer的构造函数
Constructor constructor =String.class.getConstructor(StringBuffer.class);
//反射创建对象
String str2=(String) constructor.newInstance(new StringBuffer("abc"));
System.out.println(str2);
FansheDian pt1=new FansheDian(3, 5);
Field fieldY=pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1));
//对私有成员属性的访问
Field fieldX=pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
changeStringValue(pt1);
System.out.println(pt1);
//调用方法
Method methodCharAt=String.class.getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str1, 1));
//静态方法的调用
//System.out.println(methodCharAt.invoke(null, 1));
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[]{"a","b","c"};
System.out.println("数组比较1:"+(a1.getClass()==a2.getClass()));
// System.out.println("数组比较2:"+(a1.getClass()==a3.getClass()));
// System.out.println("数组比较3:"+(a1.getClass()==a4.getClass()));
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
Object aObj1=a1;
Object aObj2=a4;
//基本数据类型不是Object
//Object[] aObj3=a1;
Object[] aObj4=a3;
Object[] aObj5=a4;
//打印a1和a4
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
Object obj=null;
printObject(a4);
}
private static void printObject(Object obj) {
Class clazz=obj.getClass();
if(clazz.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);
}
}
private static void changeStringValue(Object obj) throws RuntimeException, 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);
}
}
}
}
class TestArg{
public static void main(String[] s){
for(String str:s){
System.out.println(str);
}
}
}
package com.qba.fanshe;
public class FansheDian {
private int x;
public int y;
public String str1="ball";
public String str2="basketball";
public String str3="itcast";
public FansheDian(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return str1+":"+str2+":"+":"+str3;
}
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;
}
}
总结:1.对私有成员属性的访问:成员名.setAccessible(true);即暴力访问
2.访问静态方法:获得的方法变量.invoke(null, 参数))
四、ArrayList与HashSet的比较
我们知道ArrayList实现的是List接口;HashSet实现的是Set接口,不能存储重复元素,但重复元素是如何定义的呢?张孝祥老师对此进行了详细分析:
package com.qba.fanshe;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class FansheDemo2 {
public static void main(String[] args) {
Collection collection1=new ArrayList();
Collection collection2=new HashSet();
FansheDian pt1=new FansheDian(3, 3);
FansheDian pt2=new FansheDian(5, 5);
FansheDian pt3=new FansheDian(3, 3);
collection1.add(pt1);
collection1.add(pt2);
collection1.add(pt3);
collection1.add(pt1);
collection2.add(pt1);
collection2.add(pt2);
collection2.add(pt3);
collection2.add(pt1);
System.out.println(collection1.size());
System.out.println(collection2.size());
}
}
输出结果为:
4
3
覆写FansheDian中的equals()方法,要求如果两个FansheDian的对象中的x和y分别相等,则认为这两个对象相等:
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FansheDian other = (FansheDian) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
输出结果没有改变,在覆写hashCode()的方法,如果 两个FansheDian的对象中的x和y分别相等,则认为这两个对象的hashCode()相等:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
输出结果为:
4
2
因此,HashSet中的元素是否重复,查看的是hashCode()是否相等。
五、JavaBean和内省
JavaBean特殊的java类,方法符合某种特殊的规定:去调set和get后就得到类的属性(去掉后如果第二个字母是小写,则把第一个字母变成小写)
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则如果要在两个模块之间传递多个信息,可以讲这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象,这些信息在类中用私有字段来存储,如果读取或者设置这些字段的值,则需要通过一些相应的方法来访问,JavaBean的属性是根据其中的setter和getter方法来确定的而不是根据其中的成员变量。如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小写
总之,一个类被当做javaBean使用时,JavaBean的属性石根据方法名推断出来的,它根本看不到java类内部的成员变量
一个符合JavaBean特点的类可以当做普通类一样使用,但把它当做JavaBean用肯定需要带来一些额外的好处:
在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作
JDK中提供了对JavaBean进行操作的一些API,这套API就成为内省。用内省这套API操作JavaBean比用普通类方式更方便.张老师对JavaBean的复杂内省操作代码如下:
package cn.itcast.day1;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
public class IntroSpectorTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ReflectPoint pt1 = new ReflectPoint(3,5);
String propertyName = "x";
//"x"-->"X"-->"getX"-->MethodGetX-->
Object retVal = getProperty(pt1, propertyName);
System.out.println(retVal);
Object value = 7;
setProperties(pt1, propertyName, value);
System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());
BeanUtils.setProperty(pt1, "x", "9");
System.out.println(pt1.getX());
/*
//java7的新特性
Map map = {name:"zxx",age:18};
BeanUtils.setProperty(map, "name", "lhm");
*/
BeanUtils.setProperty(pt1, "birthday.time", "111");
System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
PropertyUtils.setProperty(pt1, "x", 9);
System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());
}
private static void setProperties(Object pt1, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodSetX = pd2.getWriteMethod();
methodSetX.invoke(pt1,value);
}
private static Object getProperty(Object pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
/*PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);*/
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds){
if(pd.getName().equals(propertyName))
{
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break;
}
}
return retVal;
}
}
ReflectPoint代码如下:
package cn.itcast.day1;
import java.util.Date;
public class ReflectPoint {
private Date birthday = new Date();
private int x;
public int y;
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
@Override
public String toString(){
return str1 + ":" + str2 + ":" + str3;
}
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;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
张老师的视频不仅仅教我们怎么使用类的方法,还帮助我们分析类方法的原理及实现过程,并带着我们自己定义相同功能的类。张老师的视频不是一遍两遍能弄明白的,每当我们学完一个阶段回过头看张孝祥老师的视频都会有新的收获。