转--黑马程序员-Java学习笔记(反射)

  1. 静态导入

package com.itheima.day1;

import static java.lang.Math.*;//导入Math类中的所有静态方法

public class StaticImport {

    public static void main(String[] args)

    {        

        int y = Math.abs(-6);    //导入前需要添加Math类    

        int z = abs(-9);        //导入后可以直接调用静态方法

        System.out.println(z);

        System.out.println(y); 

    }

}

 

总结:静态导入只是在原来导入的基础上多加了一个static的修饰符,这样就可以直接导入该类中所有的静态方法

格式:import static java.lang.Math.*;

 

2.可变参数与增强for循环

package com.itheima.day1;

public class VariableParameter {

    public static void main(String[] args)

    {

        int x = add(1,3,5,6);

        int y = add(2,4,5);

        System.out.println(x);

        System.out.println(y);        

    }

    public static int add(int x,int...args)//可变参数

    {

        int sum = x;

        //普通for循环

        /*

        for(int i =0;i<args.length;i++)

        {

            sum = (sum+args[i]);            

        }

        */

        //增强的for循环

        for(int arg : args)        //增强的for循环

        {

            sum+=arg;

        }        

        return sum;

    }

}

总结:

可变参数,就是一个方法可以有多个同类型的参数的缩写方式;

格式:func(int...args)//int可以换成各种其他类型,args

增强的for循环,可以将for循环简写

格式:for(int arg:args)//循环遍历args的每一个参数

 

3.自动装箱与拆箱&享元模式

package com.itheima.day1;  

public class AutoBox {  

    public static void main(String[] args)

    {

        Integer iObj = 3;//自动装箱,将3封装为Integer对象

        System.out.println(iObj+12); //自动拆箱

        

        //享元模式 flyweight Pattern

        //采用一个共享来避免大量拥有相同内容对象的开销

        //经典范例:String对象

        Integer i1 = 126;

        Integer i2 = 126;

        System.out.println(i1 == i2); //结果为true,说明其指向同一个对象

        

        Integer i3 = 129;

        Integer i4 = 129;

        System.out.println(i3 == i4); //结果为false,超过128,享元模式失效   

    }

}

 

总结:

自动装箱:将一个基本数据类型自动包装为对应的类对象

 Integer x = 3;//将一个int类型数据3直接自动装箱为一个Integer对象x

自动拆箱:将一个基本数据包装类对象自动拆解为基本数据类型

System.out.println(x+12); //直接将Integer对象x自动拆箱为一个int类型数据

享元模式:

对于那些经常使用到的对象,建立专门的一块区域存储,后续再建立该对象,直接引用其地址即可,不需要重新创建对象,可以节省内存开销.

具体实例:

String x = "hello";

String y = "hello";

//x与y的值相等,都指向hello所在的内存区域

 

4.枚举类

用抽象类模拟枚举类

package com.itheima.day1;

public class EnumTest {

    public static void main(String[] args)

    {

        WeekDay weekDay = WeekDay.MON;

        System.out.println(weekDay.nextDay());        

    }  

}

abstract class WeekDay //普通的Java类定义枚举

{

    private WeekDay(){}

    /*    public WeekDay nextDay()    //if else方法

    {

        if(this == SUN)

            return MON;

        else

            return SUN;

    }

*/

    //采用抽象方法nextDay()将大量的if else语句转化为独立的方法

    //因为WeekDay是一个抽象方法,因此需要用匿名内部类建立其实例对象

    //每一个都是WeekDay的子类

    public final static WeekDay SUN = new WeekDay(){

        public WeekDay nextDay()

        { 

            return MON;

        }

    };

    public final static WeekDay MON = new WeekDay(){

        public WeekDay nextDay()

        {

            return SUN;

        }

    };

    public abstract WeekDay nextDay();

    public String toString()

    {

        return this==SUN?"Sunday":"Monday";

    }

}

枚举的基本应用:

package com.itheima.day1;  

public class EnumTest {

    public static void main(String[] args)

    {

        WeekDay1 weekDay = WeekDay1.MON;

        System.out.println(weekDay.nextDay());

        

        WeekDay weekDay2 =WeekDay.FRI;

        System.out.println(weekDay2);

        System.out.println(weekDay2.name());                //返回对象名称

        System.out.println(weekDay2.ordinal());                //返回enum类中的序号

        System.out.println(WeekDay.valueOf("SUN").toString());    //

        System.out.println(WeekDay.values().length);            //返回enum类中的对象个数

    }

 

    public enum WeekDay{    //内部枚举类         

        SUN(2),MON(),TUE,WED,THU,FRI,SAT;    //必须放在最前         

        private WeekDay(){System.out.println("First!!!");};        //必须是private

        private WeekDay(int day){System.out.println("2nd!!!");};

    }

    

    //枚举只有一个成员时,可以作为一种单例的实现方式

    

    public enum TrafficLamp{

        RED(30){//调用父类带参数的构造方法

        //每个变量都是父类的一个子类,因此需要实现父类的抽象方法

            public TrafficLamp nextLamp()

            {

                return GREEN;

            }

        },

        GREEN(45){

            public TrafficLamp nextLamp()

            {

                return YELLOW;

            }

        },

        YELLOW(5){

            public TrafficLamp nextLamp()

            {

                return RED;

            }

        };

        public abstract TrafficLamp nextLamp();

        private int time;

        private TrafficLamp(int time)

        {

            this.time = time;

        }

    }

}

5.反射

得到字节码的方式:

  1. Class cls1 = Date.class;//得到Date类的字节码
  2. new Date().getClass() //
  3. 3.Class.forName("java.lang.String")//返回String类的字节码

九个预定义class实例对象:

八个基本数据类型+void

Constructor类代表某个类中的一个构造方法

package com.itheima.day1; 

import java.lang.reflect.Constructor; 

public class ReflectTest { 

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

    {

        String str1 = "abs";

        Class cls1 = str1.getClass();

        Class cls2 = String.class;

        Class cls3 = Class.forName("java.lang.String");        

        System.out.println(cls1 == cls2); //true//所有String对象的字节码是一致的

        System.out.println(cls1 == cls3);    //true//三种方法获得的字节码都是一致的

        

        System.out.println(cls1.isPrimitive());         //false//String不是基本数据类型

        System.out.println(int.class.isPrimitive());    //true

        System.out.println(int.class == Integer.class);    //false//Integer字节码跟int不一致

        System.out.println(int.class == Integer.TYPE);    //true

        System.out.println(int[].class.isPrimitive());    //false//数组不是基本数据类型

        System.out.println(int[].class.isArray());        //true

        

        //new String(new StringBuffer("abc"))

        //编译时不看代码的执行,只是翻译成机器代码

        

        //获得构造方法

        Constructor<String> cons = String.class.getConstructor(StringBuffer.class);

        

        //错误,不能这样传递,因为cons获得的构造方法必须带StringBuffer的参数

        //String str = cons.newInstance("abc");

        

        //创建实例对象

        String str = cons.newInstance(new StringBuffer("abc"));

        System.out.println(str);

    }

}

//总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[],void

/*

* class也有newInstance()方法

class ->constructor ->new object

简化后:

class ->new object

 

*/

//反射会导致程序性能严重下降

Field:反射类的成员变量,可以通过getField()方法获得

{

        ReflectPoint pt1 = new ReflectPoint(3,5);

        Field fieldy = pt1.getClass().getField("y");//只能得到可见的参数        

        //filedy不是对象上的变量,而是类上的变量,因此用filedy.get(对象名)来获取其值;

        System.out.println(fieldy.get(pt1));//得到具体实例对象中的成员变量值

        

        Field fieldx = pt1.getClass().getDeclaredField("x");//可以看到private修饰的参数值

        fieldx.setAccessible(true);            //暴力获取private修饰的值

        System.out.println(fieldx.get(pt1));

        

        changeStringValue(pt1);

        System.out.println(pt1);

}

    private static void changeStringValue(Object obj)throws Exception

    {

        Field[] fields = obj.getClass().getFields();//得到对象的所有可见成员变量

        for(Field field:fields)//增强的for循环,遍历所有成员变量

        {

            

            //if(field.getType().equals(String.class))

            if(field.getType() == String.class)//因为是字节码,因此直接用等号判断

            {

                //得到String成员变量的值

                String oldValue = (String)field.get(obj);

                

                //将String成员变量中的所有出现的b字母替换成a字母

                String newValue = oldValue.replace('b','a');

                

                //将源对象中的该成员变量值替换

                field.set(obj, newValue);

                

            }

        }        

    }

原字符串:

替换后的结果:

 

Method:反射类的方法,可以用getMethod方法获得

{

        String str1 ="xyz";        

        //getMethod():第一个参数为想要获取的方法名,第二个参数为方法接受的参数类型

        Method methodAt = String.class.getMethod("charAt", int.class);

        System.out.println(methodAt.invoke(str1,1));

        

        //用反射方式调用类的main方法

        //当要启动的类名不确定,需要临时传递时,可以使用这种方式

        String startClassName = args[0];

        Method mainMethod = Class.forName(startClassName).getMethod("main", String[].class); 

 

        //mainMethod.invoke(null,new String[]{"111","222","333"});

        //会报错,因为将参数拆成了3个        

        mainMethod.invoke(null,(Object)new String[]{"111","222","333"});

        //告诉编译器我传递的是一个对象,不要将其拆包

        //因为main方法是一个静态方法,因此不需要实例对象就可以调用

}

class TestArguments{

    public static void main(String[] args)

    {

        for(String arg : args)

            System.out.println(arg);

    }

}

运行结果:

 

数组的反射:

{

int[] a1 = new int[]{1,5,2};

        int[] a2 = new int[4];

        int[][] a3 = new int[2][3];

        String[] a4 = new String[]{"b","x","a"};

        //由打印结果可知,两者字节码一致

        System.out.println(a1.getClass() == a2.getClass());

        

        //a1跟a3类型不同, 不能比较

        //System.out.println(a1.getClass() == a3.getClass());

        

        //a1跟a4类型不同, 不能比较

        //System.out.println(a1.getClass() == a4.getClass());

        

        //由打印结果可知,a1的字节码为[I,代表其为int类型的数组

        System.out.println(a1.getClass().getName());

        

        //父类的字节码为Object

        System.out.println(a1.getClass().getSuperclass().getName());

        

        System.out.println(a3.getClass().getName());

        //String类的父类字节码为Object

        System.out.println(a4.getClass().getSuperclass().getName());

        

        Object obj1 = a1;

        Object obj2= a4;

        //Object[] obj3 = a1;//不可以,因为a1是int类型的数组!

        Object[] obj4 = a4;//可以,因为String是一个Object

        Object[] obj5 = a3;//可以,因为int[]是一个Object

        

        System.out.println(a1);

        System.out.println(a4);

        System.out.println(Arrays.asList(a1));//按照Object转换a1,因此还是异常显示(jdk1.5)

        System.out.println(Arrays.asList(a4));//按照Object[]转换a4,因此可以正常显示(jdk1.4)

        

        printObj(a4);

        printObj("zxy");

        

    }

    private static void printObj(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);

        }

        

    }

运行结果:

true

[I

java.lang.Object

[[I

java.lang.Object

[I@14318bb

[Ljava.lang.String;@ca0b6

[[I@14318bb]

[b, x, a]

b

x

a

zxy

 

ArrayList与HashSet的比较及HashCode的分析

public class ReflectTest2 { 

    public static void main(String[] args) {

//ArrayList按照先后顺序存储

        //Collection cl = new ArrayList();        

        Collection cl = new HashSet();

        //存储时按照HashCode值来划分区域,存储到不同的区域,

        //查找时,先算出对象的hashcode值,再到相应的区域寻找,这样就提高了查找效率

        ReflectPoint r1 = new ReflectPoint(3,3);

        ReflectPoint r3 = new ReflectPoint(4,5);

        ReflectPoint r2 = new ReflectPoint(2,3);

        ReflectPoint r4 = new ReflectPoint(3,3);

        

        cl.add(r1);

        cl.add(r2);

        cl.add(r3);

        cl.add(r4);

        

        cl.remove(r1);    //没有修改的时候,成功删除,size变为2        

        System.out.println(cl.size());

        

        //当一个对象被存储进HashSet后,就不能更改这个对象中参与哈希值的字段了!

        //会造成内存泄露!!!

        cl.add(r1);

        r1.y = 8;        

        cl.remove(r1);    

        //修改参与计算哈希值的字段后,删除失败!

        //size仍然为3!长此以往,会造成内存泄露!

        

        System.out.println(cl.size());

        //没有重写HashCode方法时,size = 4

        //重写hashCode方法后,size = 3

                

    }

}

运行结果:

反射的作用:实现框架功能

调用别人的类,为工具类

自己的类被别人调用,为框架

        //读取配置文件来实现创建对象

        InputStream is = new FileInputStream("config.properties");        

        //读取键值对,用Properties比较方便

        Properties prop = new Properties();

        prop.load(is);        

        is.close();        //释放对象关联的系统资源        

        String className = prop.getProperty("className");

        Collection cl = (Collection)Class.forName(className).newInstance();

当config.properties为ArrayList时:

运行结果:

当config.properties为HashSet时:

运行结果:

 

资源和配置文件的管理:

用类加载器的方式:

/*

         * getRealPath()

         * 一定要用绝对路径,但是该绝对路径不是硬编码,而是运算得出         

*/   

        //读取配置文件来实现创建对象

        //InputStream is = new FileInputStream("config.properties");//相对路径        

        //利用类加载器加载配置文件

        //InputStream is = ReflectTest2.class.getClassLoader().getResourceAsStream("com/itheima/day1/config.properties");

        //InputStream is = ReflectTest2.class.getResourceAsStream("config.properties");//简化的相对路径

        InputStream is = ReflectTest2.class.getResourceAsStream("/com/itheima/day1/config.properties");//斜杆开头,需要绝对路径

 

总结:

1.静态导入:

静态导入只是在原来导入的基础上多加了一个static的修饰符,这样就可以直接导入该类中所有的静态方法,导入后可以直接调用静态方法,不需要在方法前添加类名

格式:import static java.lang.Math.*;

2.可变参数,增强的for循环

   可变参数,就是一个方法可以有多个同类型的参数的缩写方式;

格式:func(int...args)//func方法可以有0~N个int参数

 

   增强的for循环,可以将for循环简写

   格式:for(int arg:args)//循环遍历args的每一个参数

   3.自动装箱与拆箱&享元模式

自动装箱:将一个基本数据类型自动包装为对应的类对象

 Integer x = 3;//将一个int类型数据3直接自动装箱为一个Integer对象x

自动拆箱:将一个基本数据包装类对象自动拆解为基本数据类型

System.out.println(x+12); //直接将Integer对象x自动拆箱为一个int类型数据

享元模式:

对于那些经常使用到的对象,建立专门的一块区域存储,后续再建立该对象,直接引用其地址即可,不需要重新创建对象,可 以节省内存开销.

具体实例:

String x = "hello";

String y = "hello";

//x与y的值相等,都指向hello所在的内存区域

4.枚举类

   public enum TrafficLamp{

        RED(30){//调用父类带参数的构造方法

        //每个变量都是TrafficLamp父类的一个子类,因此需要实现父类的抽象方法

            public TrafficLamp nextLamp()

            {

                return GREEN;

            }

        }, 

        public abstract TrafficLamp nextLamp();

        private int time;

        private TrafficLamp(int time)

        {

            this.time = time;

        }

    }

}

5.反射

Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。

先要得到字节码,三种方式:

注:字节码文件是经过编译器预处理过的一种文件,是JAVA的执行文件存在形式,它本身是二进制文件,但是不可以被系统直接执行,而是需要虚拟机解释执行,由于被预处理过,所以比一般的解释代码要快,但是仍然会比系统直接执行的慢。

  1. Class cls1 = Date.class;//得到Date类的字节码
  2. new Date().getClass() //
  3. 3.Class.forName("java.lang.String")//返回String类的字节码

得到反射类的构造方法:

Constructor<String> cons = String.class.getConstructor(StringBuffer.class);        

        //错误,不能这样传递,因为cons获得的构造方法必须带StringBuffer的参数

        //String str = cons.newInstance("abc");        

        //通过得到的构造方法cons创建实例对象,通过newInstance();方法

        String str = cons.newInstance(new StringBuffer("abc"));

得到反射类的成员变量:

  Field fieldy = pt1.getClass().getField("y");//只能得到可见的参数        

        //filedy不是对象上的变量,而是类上的变量,因此用filedy.get(对象名)来获取其值;

        System.out.println(fieldy.get(pt1));//得到具体实例对象中的成员变量值        

        Field fieldx = pt1.getClass().getDeclaredField("x");//可以看到private修饰的参数值

得到反射类的方法:

 //getMethod():第一个参数为想要获取的方法名,第二个参数为方法接受的参数类型

        Method methodAt = String.class.getMethod("charAt", int.class);

//使用该方法时,用invoke方法

        System.out.println(methodAt.invoke(str1,1));

 

6.HashSet()使用注意事项:

修改参与计算哈希值的字段后,会导致删除失败!长此以往,会造成内存泄露!

参与计算哈希值的字段千万小心修改!

 来源: http://blog.163.com/ljj1219@126/blog/static/143633241201461451655957/

posted @ 2016-12-12 14:20  mayya  阅读(106)  评论(0)    收藏  举报