------- android培训、java培训、期待与您交流! ----------
基础加强2
一,反射
5,Method类(成员方法):
1)示例:
A,方法的调用:方法对象名.invoke()
//String str = "abc";
//str。charAt(1); 的另一种写法
Method methodCharAt = String.class .getMethod("chatAt",int.class) ;
//chatAt--》使用方法的名字 int.class --》调用的方法参数(个数,类型)
methodCharAt.invoke(str,1)
//str--》字符串对象 1--》传进的参数
B,静态方法的调用:方法对象名.invoke(null,。。。)
2)jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
例如:methodCharAt.invoke(str,new Object[]{2})
6,用反射调用类的main方法
1)好处:当我们在编译时没有明确要调用的类名,只给出反射的数据时,我们可以使用反射的数据,利用Class的forName的方法获得调用的类名
2)示例
class TestArguments
{
public static void main(String[] args)
{
for (String arg : args )
{
System.out.println(arg);
}
}
}
//普通要调用此类的main方法:TestArguments.main(new String[]{"111"});
//反射调用方式
String diaoyongClassName = args[0];
Method mainMethod =
Class.forName(diaoyongClassName).getMethod
("main",String[].class);mainMethod.invoke(null,new Object[]{new String[]{"111","222'}});
//每一个数组的父类都是Object[]
7,数组的反射:具有相同元素类型和维度的数组属于同一Class类,即具有相同的字节码。获得的此Class类名为【I,父类是示例:
1)Int[] a1 = new int[3];
Int[] a2 = new int[4];
Int[][] a3 = new int[2][3];
String[] a4 = new String[4];
a1.getclass()==a2.getclass()-->true
a2.getclass()==a3.getclass()-->false
a1.getclass()==a4.getclass()-->false
获得的此Class类名为【I,一维,二维父类是Object
2)Object aobj1= a1--》true
Object aobj2= a2--》true
Object[] aobj3= a1--》false //因为a1的类型是int。不属于Object
Object[] aobj4= a3--》true
Object[] aobj5= a4--》true
3)Int[] a1 = new int[3]{2,1,3};-->Sys(a1):表示地址值
String[] a4 = new String[4]{“123”};0-->Sys(a4):表示地址值
要直接打印数组:用Arrays数组工具类
Arrays.asList(a1)-->对于int型不会打印,还是打印地址值
Arrays.asList(a4)-->对于String型,可以,因为String属于Object类
8,反射的作用:实现框架功能
1)什么是框架:当我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。是sun公司提供的一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。
2)示例:
public static void main(String[] args) throws Exception{
Properties props = new Properties();
//InputStream ips = new FileInputStream("config.properties");
//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream
("cn/itcast/javaenhance/config.properties");
//InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream
("/cn/itcast/javaenhance/config.properties");
props.load(ips);
Ips.close();
String className = props.getProperty("className");
Class clazz = Class.forName(className);
Collection collection = (Collection)clazz.newInstance();
//Collection collection = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3,3);
ReflectPoint pt2 = new ReflectPoint(5,5);
ReflectPoint pt3 = new ReflectPoint(3,3);
collection.add(pt1);
collection.add(pt2);
collection.add(pt3);
collection.add(pt1);
System.out.println(collection.size());
}
二,内省
1,JavaBean定义:一种特殊的Java类,主要用于传递数据信息,这种java类中的主要用于访问私有的字段,且方法名符合某种命名规则。
2,特点:
A,在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方进行
操作,别人都这么用和要求这么做,那你就没什么挑选的余地!
B,JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你
自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api
操作JavaBean比用普通类的方式更方便。
3,示例:
public static void main(String[] args) throws Exception{
ReflectPoint pt1 = new ReflectPoint(3,5);
Object retVal = getProperty(pt1);
System.out.println(retVal);
PropertyDescriptor pd2 = null;
String propertyName = "y";
Object value = 7;
SetProperty(pt1, propertyName, value);
//先通过调用普通java类的方法的方式获得结果,然后在这之前插入BeanUtil的get和set操作
System.out.println(BeanUtils.getProperty(pt1, "y"));
BeanUtils.setProperty(pt1, "y", "99");
System.out.println(pt1.getY());
PropertyUtils.setProperty(pt1, "y", 999);
System.out.println
(PropertyUtils.getProperty(pt1,"y").getClass().getName());
}
private static Object getProperty(ReflectPoint pt1) {
Object retVal = null;
PropertyDescriptor pd = null;
try {
pd = new PropertyDescriptor("y",pt1.getClass());
retVal = pd.getReadMethod().invoke(pt1);
} catch (Exception e) {
e.printStackTrace();
}
return retVal;
}
private static void setProperty(Object pt1, String propertyName,
Object value) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor pd :pds){
if(pd.getName().equals(propertyName)){
pd.getWriteMethod().invoke(pt1,value);
break;
}
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
三,深入泛型<T>
1,背景:泛型没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
2,创建泛型:Class<String> clazzString1 = String.class;
Constructor<String> constructor1 = clazzString1.getConstructor(StringBuffer.class);
Class c = ((Test)obj).getClass();-->false
c.newInstance().func();-->false
3,通配符?
1)作用:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
2)示例:
public static void printCollection(Collection<?> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
//错误,因为它不知自己未来匹配就一定是String
cols.size();
//这里如果是cols.add("string");就会产生错误,因为它匹配的不一定是String类型
//而此方法与类型参数没有关系
cols = new HashSet<Date>();
}
4,定义泛型
交换数组中的两个元素的位置的泛型方法语法定义
static <E> void swap(E[] a, int i, int j) {
E t = a[i];
a[i] = a[j];
a[j] = t;
}
5,类型参数的类型推断
方法
1)当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么 根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) static <E> void swap(E[] a, int i, int j)
2)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) static <T> T add(T a, T b)
3)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) static <T> void fill(T[] a, T v)
4)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f) static <T> T add(T a, T b)
5)参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5])
copy(new Vector<String>(), new Integer[5])
static <T> void copy(T[] a,T[] b);
static <T> void copy(Collection<T> a , T[] b);
四,类加载器:类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap
示例:编写自己的类加载器
mport java.io.*;
import java.lang.reflect.*;
public class MyClassLoader extends ClassLoader
{
private String path = null;
public MyClassLoader(String path) throws Exception//检查文件是否存在
{
File f = new File(path);
if(!f.isDirectory())
{
throw new RuntimeException(path + " is not a directory");
}
this.path = path;
}
public Class findClass(String name) //throws Exception //为什么不能抛出
{
try
{
File f = new File(path,name.substring(name.lastIndexOf('.')+1) + ".class");
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte [] buf = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name,buf,0,buf.length);
}catch(Exception e)
{
throw new ClassNotFoundException(name + " is not found!");
}
return null;
}
public static void cypher(InputStream istream,OutputStream ostream) throws Exception
{
//下面这段代码可能遇到255的字节,当成byte就成了-1
int b = 0;
while((b = istream.read()) != -1)
{
ostream.write(((byte)b) ^ 0xff);
}
}
public static void main(String [] args) throws Exception
{
//下面省略了错误检查
if(!args[0].endsWith("class"))
{
ClassLoader loader = new MyClassLoader(args[1]);
Class cls = loader.loadClass(args[0]);
Method m = cls.getMethod("test");
m.invoke(cls.newInstance());
return;
}
else
{
FileInputStream fis = new FileInputStream(args[0]);
File f = new File(args[1], new File(args[0]).getName());
//不用检查目录最后是否有目录分割符
FileOutputStream fos = new FileOutputStream(f);
cypher(fis,fos);
fis.close();
fos.close();
}
}
}
五,代理
1,定义:当我们要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能时,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,这时我们编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
2,动态代理技术:
private static void test() {
Vector v = new Vector();
class MyInvocationHandler implements InvocationHandler
{
Collection target = null;
public Collection bind(Collection target)
{
this.target = target;
Collection proxy1 = (Collection)Proxy.newProxyInstance(
ProxyTest.class.getClassLoader(),
new Class[]{Collection.class} ,
this);
return proxy1;
}
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("begin " + method.getName());
Object retval = method.invoke(target, args);
System.out.println("end" + method.getName());
return retval;
}
}
MyInvocationHandler handler = new MyInvocationHandler();
Collection proxy1 = handler.bind(v);
System.out.println(proxy1.getClass().getName());
proxy1.add("abc");
proxy1.add("xyz");
System.out.println(proxy1.size());
}
总结:终于结束了基础加强的学习,在张老师特殊的口音下,我真的学了不少东西,有很多都是需要细细琢磨的。主要学习了反射,深入学习了泛型,类加载器,动态代理,多线程的新知识,线程池等等,这些都是重要的基础内容,其中的代码更是要求熟练掌握,只有学好基础,才能更近一步