• Java高新技术


    1  JDK5的新特性

    1.1 静态导入

          在API中那些不需要new对象的类,可以在类文件的开头,import static java.lang.Math.*;这里把Math中的所有的静态方法都导入了,在类中不需要调用Math类就能直接用Math的方法了

    package cn.wjd.staticimport;
    
    import static java.lang.Math.*;
    public class StaticImport {
        public static void main(String[] args) {
            System.out.println(min(3,5));//3
            System.out.println(max(3,5));//5
        }
    }

    1.2 可变参数

        在方法的形参上面,可以定义B个类型的参数,比较相加的方法的话,用了可变参数的方法后,以后只要数据类型相同,有多少个数相加都可以用这个方法,而不用像以前那个去Overload方法.

       可变参数的特点:  只能定义在参数的最后位置        ...位于变量类型和变量名之间       调用可变参数的时候,编译器为该方法创建了一个数组,

    package cn.wjd.staticimport;
    
    public class VariableParameter {
        public static void main(String[] args) {
            
            System.out.println(add(1,1,1,1,1));
            System.out.println(add(2,1,1,4));
        }
        public static int add(int x,int ...args){
            int sum = x;
            for (int arg : args){
                sum = sum + arg;
            }
            return sum;
        }
    }

    上面的可变参数的代码中,args这个数组中的int类型,是不包括前面的int x的.....

    1.3 for循环增强

        格式   for(type 变量名 : 集合变量名){..........}

    注意事项:
    迭代变量必须在( )中定义!
    集合变量可以是数组或实现了Iterable接口的集合类。

     

    1.4 基本数据类型的自动拆箱与装箱

    (1)Integer,Byte,Double,Float,Short,Long都属于Number类的子类,Number类本身提供了一系列的返回以上6种数据类型的操作

    (2)Character属于Object的直接子类

    (3)Boolean属于Object的直接子类

    (4)装箱:将一个基本数据类型变成包装类   拆箱:将一个包装类变成基本数据类型

    (5)包装类运用的最多的是将字符串变为基本数据类型

    Integer类(字符串变int)                                                                                 Float类(字符串变float型)

    public static int parseInt(String s)throws NumberFormatException               public static float parseFloat(String s)throws NumberFormatExeption

    1.5 枚举

    枚举就是让某些类型的变量的取值,只能是固定的几个值,不然的话编译器就会报错,枚举可以在编译器编译的时候控制源程序中填写的非法的值

    如果要用普通的类来实现枚举的功能

    (1)私有的构造方法  (2)每个元素分别用一个公有的静态成员变量表示。(3)可以有若干公有方法或抽象方法

    枚举就是一个特殊的类,其中定义的元素,其实就是对象,一下的代码WeekDay设置为抽象类的话,在SUN和MON这两个对象上,用内部类,直接将抽象方法进行了复写

    package enumdemo;
    //将类抽象,里面nextDay类也要抽象了,由于SUN和MON都是对象,我们直接内部类来完成功能
    public abstract class WeekDay {
        private WeekDay(){}
        public static final WeekDay SUN = new WeekDay(){
            public WeekDay nextDay(){
                return MON;
            }
        };
        public static final WeekDay MON = new WeekDay(){
            public WeekDay nextDay(){
                return SUN;
            }
        };
        public abstract WeekDay nextDay();/*{
            if(this==SUN)
                return MON;
            else
                return SUN;
        }*/
        public String toString(){
            return this == SUN?"SUN":"MON";
        }
    }

    实现带有构造方法的枚举

    ·枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
    ·枚举元素必须位于枚举体中的最开始部分,枚举元素列表的最后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器会报告错误。
    ·带构造方法的枚举:
    构造方法必须定义成私有的
    如果有多个构造方法,将根据枚举元素创建时所带的参数决定选择哪个构造方法创建对象。
    枚举元素MON和MON()的效果一样,都是调用默认的构造方法。

    package enumdemo;
    
    public class EnumTest2 {
        public static void main(String[] args) {
            WeekDay2 day = WeekDay2.SUN;
            System.out.println(day.valueOf("SUN"));
            System.out.println(day.values().length);
    
        }
    
        public enum WeekDay2{//枚举就是对象,调用对象的时候肯定会有构造方法的调用,调用那个构造方法就看枚举的参数了
            SUN,MON(1),TUE(2),WED(3),THI,FRI,SAT;
            private WeekDay2(){
                System.out.println("11111.....");
            }
            private WeekDay2(int i){
                System.out.println("22222.......");
            }
        }
    }

    上述代码中,所有的星期都是对象,那么对象在初始化的时候,会调用类的构造方法,观察输出语句可以发现,在初始化的时候,无参数的枚举类型调用无参的构造函数,有参的构造方法会去调用有参的构造函数进行初始化.

    2  JavaBean内省

    java.beans中 PropertyDescriptor类中常用的方法

    构造方法 描述
    public PropertyDescriptor(String propertyName, Class<?> beanClass) throws IntrospectionException 通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。因此如果参数名为 "fred",则假定 writer 方法为 "setFred",reader 方法为 "getFred"(对于 boolean 属性则为 "isFred")。注意,属性名应该以小写字母开头,而方法名称中的首写字母将是大写的。
    方法 描述
    public Method getReadMethod() 获得应该用于读取属性值的方法。
    public void setReadMethod(Method readMethod)throws IntrospectionException
    设置应该用于读取属性值的方法。

    JavaBean是一种特殊的Java类,说通俗点,它就是来访问类的各种字段,并且这种字段要符合某种命名的规定.

    如果两个模块之间要传递多个信息,就可以将这些信息封装在JavaBean中,这种JavaBean的实例化对象称为值对象(Value Object,简称VO)

    JavaBean中的属性是根据类中的get,set方法来确定的,去掉get,set前缀,剩余的部分就是属性的名字,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。

    setId()的属性名为 id

    setCPU()的属性名是CPU

    一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。

    下面的代码是对JavaBean简单的内省的操作

    步骤  (1)创建一个需要被访问属性的属于那个类的对象

           (2)创建PropertyDescriptor类的构造函数,在构造函数中传入属性类的属性的名字,该类的class

           (3)使用PropertyDescriptor类中的方法(上面表格中的方法),进行设置和获取属性值的操作.

    package cn.javabean;
    
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Method;
    
    class Person{
        private String name;
        private int age;
    
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        
    }
    public class JavaBeanDemo {
        public static void main(String[] args)throws Exception {
            Person p = new Person("wjd",25);
            PropertyDescriptor pd1 = new PropertyDescriptor("name",p.getClass());
            Method methodName = pd1.getReadMethod();
            Object retVal = methodName.invoke(p);
            System.out.println(retVal);
            
            Object value = 8;
            PropertyDescriptor pd2 = new PropertyDescriptor("age",p.getClass());
            Method methodAge = pd2.getWriteMethod();
            methodAge.invoke(p,value);
            System.out.println(p.getAge());
        }    
        
    
    }

    使用BeanUtils工具类来操作JavaBean

    这个工具类需要用到两个java类的jar包,1

    用该工具来操作JavaBean的时候,属性的类型会自动转化,这个功能真心很贴心,比如在web开发中,用户输入的数字,都是以字符串的形式发给服务器的

    3,反射的加强(数组与反射)

    (3.1)对接受数组类型的成员方法进行反射

    步骤:(1) 得到一个数组的元素,这个假设数组是String类型的,那个得到的元素是String类型的

          (2)用这个元素去得到数组的class对象,这里用的是Class.forName这个方式的反射

          (3)有了class,就能得到成员方法了,用getMethod()方法,在里面传入方法的名称,和数组类型的class,比如String[].class

          (4)执行方法一定要有对象,看到method,一定要想到invoke方法,在该方法中传入被反射的对象,还有参数类型,假如类是静态的话,用null,参数类型由于传入的是数组,编译器在编译的时候,会将数组拆开,解决方法可以直接在数组前面弄了Object,这样的话,编译器就认为是单个而不是多个了

    package cn.reflectdemo;
    
    import java.lang.reflect.Method;
    
    //对接受数组类型的成员方法进行反射
    public class ReflectArray {
        public static void main(String[] args)throws Exception{
            String str = args[0];
            Method mainMethod = Class.forName(str).getMethod("main", String[].class);
            mainMethod.invoke(null, (Object)new String[]{"a","b","c"});
        }
    }
        
    class ArrayDemo{
        public static void main(String[] args){
            for(String arg : args){
                System.out.println(arg);
            }
        }
    }
    (3.2)数组与Object的关系以及其反射的类型

    具有相同元素类型,数组的维度也相同的数组,它们的Class实例化对象相同

    基本类型的一维数组,可以当作Object来使用,不能当作Object[]来使用

    非基本数组类型的一维数组既能当Object使用,又能当Object[]使用

    (3.3)数组之间的得到class的关系

    具有相同元素类型,和相同维数的数组都属于同一个类型

    package cn.reflect;
    
    public class Array {
        public static void main(String[] args) {
            int[] a1 = new int[2];
            int[] a2 = new int[3];
            int[][] b1 = new int[2][3];
            String[] c1 = new String[3];
            System.out.println(a1.getClass() == a2.getClass());//true
            System.out.println(a1.getClass() == b1.getClass());//false
            System.out.println(a2.getClass() == c1.getClass());//false
        }
    }
    (3.4)Arrays.asList()方法处理int[]和String[]的区别

    这个上次再做去掉重复数组元素的时候,被迷惑过,这下终于被张孝祥老师点通了.下面先从代码上分析这个问题

    package cn.reflect;
    
    import java.util.Arrays;
    
    public class ArrayasList {
        public static void main(String[] args) {
            int[] a = {1,2,3};
            String[] b = {"a","b","c"};
            System.out.println(a);//[I@5eab4b89
            System.out.println(b);//[Ljava.lang.String;@3fec3fed
            System.out.println(Arrays.asList(a));//[[I@5eab4b89]
            System.out.println(Arrays.asList(b));//[a, b, c]
        }
    }

    在JDK1.4中 Arrays.asList(Object[] obj),在JDK1.5中 Arrays.asList(T... a),现在使用的是JDK1.5,这个方法将int[] a当作了一个整体来操作,而int[]是不可以当成Object[]的数组类型来处理的,所以是这个结果了[[I@5eab4b89],而String[]既可以当Object有可以当Object[].

    (3.5)Array工具类来完成对数组的反射

    步骤    1,得到对象的Class对象,用Class中的isArray方法对obj对象进行判断

             2,如果是数组的话,使用Array这个工具类中的getLength方法来得到数组的长度

             3,使用for循环,再使用Array工具类中的get()方法来打印出数组

    package cn.reflectdemo;
    
    import java.lang.reflect.Array;
    
    public class ArrayReflect {
        public static void main(String[] args) {
            int[] array = {1,2,3,4,5,6,7};
            String str = "abs";
            printObject(array);
            printObject(str);
        }
    
        private static void printObject(Object obj) {
            Class clazz = obj.getClass();
            if(clazz.isArray()){
                int leng = Array.getLength(obj);
                for(int i=0;i<leng;i++){
                    System.out.println(Array.get(obj, i));
                }
            }else{
                System.out.println(obj);
            }
        }    
    
    }
    (3.6)ArrayList_HashSet和 HashCode的区别
    package cn.enhancereflect;
    
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Properties;
    
    public class ReflectTest2 {
        public static void main(String[] args) throws Exception{
        //     = new FileInputStream("config.properties");
            //用类加载器来管理配置文件,类存在的时候,会通过classpath去寻找class文件,那样我们把配置文件放在class文件的目录下,用类加载器去管理它
            // InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/enhancereflect/config.properties");
            InputStream ips = ReflectTest2.class.getResourceAsStream("resource/config.properties");
            Properties pro = new Properties();
            pro.load(ips);
            ips.close();
            String className = pro.getProperty("className");
            Collection collections = (Collection) Class.forName(className).newInstance();
            //Collection collections = new HashSet();
            ReflectPoint pt1 = new ReflectPoint(3,3);
            ReflectPoint pt2 = new ReflectPoint(5,5);
            ReflectPoint pt3 = new ReflectPoint(3,3);
            collections.add(pt1);
            collections.add(pt2);
            collections.add(pt3);
            collections.add(pt1);
            pt1.y = 9;
        //    collections.remove(pt1);//这里假如改变了Hash中的值后,由于哈希表是分区域存储数据的
            //将Y变成9这个动作,修改的数据的位置肯定是在哈希表的另外的区域,所以当值修改后,HashSet中的元素会删除不掉
            //这个就是内存泄漏的一个现象
            System.out.println(collections.size());
        }
    
    }
    (3.7)利用反射的原理开发的简单框架

    我的理解,建立配置文件,利用配置文件key->value的关系,我们先用get(key)来做反射等操作,这样就不需要知道类的类型,或者后期只要在配置文件中操作类的类型

     

    4 类加载器

    (4.1)  类加载器的深入研究和它的委托代理机制

    我自己的理解:我们编写java类型的文件,通过编译器变成了class文件,那个在运行这些class文件的时候,需要加载中计算机中,通过一系列的处理,这样程序就能正常的运行起来,

    类加载器本身就是一个类,那个在加载的时候,一级级的往上处理的话,肯定有一个不是类的类加载器,这个类加载器我们称为爷爷,由于这样的机制存在,那么就有了java这个类加载器的关系出来了,通过学习,java有这三种类加载器,它们各司其职,加载这属于自己处理的class文件,这个机制是从下面开始往上面找,优先上面,当最后在最上面都处理不了的话,会抛出

    ClassNotFoundException异常.

    Java虚拟机中的所有类装载器采用了具有父子关系的树形结构进行组织。

    BootStrap(爷爷),ExtClassLoader(爸爸),AppClassLoader(儿子),通过上面的理解,可以判断是BootStrap这个爷爷肯定不是java类,它在JVM启动的时候,这个爷爷就可以存在了,

    (4.2) 类加载器的委托机制

    当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
    首先当前线程的类加载器去加载线程中的第一个类。
    如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
    还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

    每个类加载器加载类时,又先委托给其上级类加载器。
    当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?

    注意:

    1-每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

    2-有一道面试题,能不能自己写个类叫java.lang.System?

    答案是不能,即使写了也不会被类加载器加载。为了不让我们写System类,类加载机制采用委托机制,这样可以保证父级类加载器优先,也就是总是使用父级类加载器能找到的类,结果就是总是使用java系统自身提供的System类,而不会使用我们自己所写的System类

     

    5  代理

    (5.1)  代理的概念和作用  

    编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。

    如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。

    我自己的大白话理解,代理我们在编写Proxy这个代理类的时候,要去访问某个类中的方法,举个例子,我们代理了ArrayList这个类,需要去访问ArrayList类中的各个方法,这个时候API中的Proxy类和InvocationHandler接口就要出场了,在InvocationHandler接口中只有一个而且很重要的方法,如下图1当Proxy类和InvocationHandler接口登场的时候,其实说白了就是反射,在invoke这个方法中,对我们代理的类,进行反射处理,来得到类的对象,方法和参数,我们如果要正常访问ArrayList中的方法的时候,直接用它的对象去访问就OK了,用了代理,在invoke中都已经将那个需要被代理的类的参数都处理好了,就避开了正常访问的形式,直接通过代理的形式去访问了.

    (5.2) 创建动态类的实例化对象以及调用它的方法

    package cn.proxy;
    // 创建动态类的实例对象及调用其方法
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Collection;
    
    public class Proxy01 {
        public static void main(String[] args)throws Exception {
            //拿到动态类的对象
            Class classProxy = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
            //拿构造方法,查看API,发现Proxy中没有无参构造方法,你懂的,拿构造函数参入形参
            Constructor constructor = classProxy.getConstructor(InvocationHandler.class);
            //有构造方法后,肯定是得到类的对象了,拿对象穿实参
            Collection proxy = (Collection)constructor.newInstance(new InvocationHandler(){
    
                public Object invoke(Object arg0, Method arg1, Object[] arg2)
                        throws Throwable {
                    
                    return null;
                }
                
            });
            System.out.println(proxy);//null
            proxy.add(1);
            proxy.add(2);
            proxy.add(3);
            System.out.println(proxy.size());//报告异常
        }
    
    }

    分析上述代码出现的结果,上面的自己的大白话解释了,在InvocationHandler这个接口中,是对被我代理的类进行反射操作,来得到类的对象,方法和属性,而在上述的代码中,我只是直接复写了InvocationHandler中的invoke方法,在方法中没做任何的处理,这个时候代理Collection是不成功了.下面对InvocationHandler中的invoke方法进行代码的处理,编写目标类,这样就能成功的代理某个类了.运行结果已经可以看出proxy这个类已经访问到了目标类中的方法,已经成功代理了,具体代码如下,下面的代码实现了对于代理类创建的一步到底,非常方便.

    package cn.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.ArrayList;
    import java.util.Collection;
    
    public class Proxy02 {
        public static void main(String[] args) {
            //看了下API 这个方法很牛叉,可以对代理类的创建一步到位
            Collection proxy = (Collection)Proxy.newProxyInstance(
                    Collection.class.getClassLoader(),
                    new Class[]{Collection.class},
                    new InvocationHandler(){
                        ArrayList target = new ArrayList();
                        public Object invoke(Object arg0, Method arg1, Object[] arg2)
                                throws Throwable {
                            Object retVal = arg1.invoke(target,arg2);
                            return retVal;
                        }
                        
                    });
            proxy.add(1);
            proxy.add(2);
            proxy.add(3);
            System.out.println(proxy);//[1, 2, 3]
            System.out.println(proxy.size());// 3
        }
    }

    (5.3) 分析InvocationHandler对象的运行原理

    Object
    invoke(Object proxy, Method method, Object[] args)   target.add(1);这个两个相互比较下.

    Object proxy 与target对应; Method method方法与 add()方法对应; 方法中的参数1与args对应. 本质上代理就是在反射,看下面的代码,以add方法为例子

    $Proxy0 implements Collection
    {
         InvocationHandler handler;
         public $Proxy0(InvocationHandler handler)
         {
              this.handler = handler;
         }
         //生成的Collection接口中的方法的运行原理
         boolean add(Object obj){
              handler.invoke(this,this.getClass().getMethod("add"),obj);//这里invoke中,就是利用反射,来拿到目标中的方法,参数
         }
    }

    Tips  :

    调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求,比如getClass方法,所以它会返回正确的结果

    1


             

    6,注解

    (6.1) Annotation简介

    对元数据(Metadata)的支持,在J2SE 5.0中,这种元数据称为注解.

    在JDK1.5之后,系统中有3个内建的Annotation类型,我们可以直接使用

    @Override: 覆写的Annotation    在方法覆写的时候使用,用于保证

    @Deprecated:不赞成使用的Annotation  用来声明一个不建议使用的方法,如果在程序中使用了此方法,则在编译时将出现警告

    @Suppress Warnings:压制安全警告的Annotation 

    (6.2)为注解增加各种属性

    ·什么是注解的属性
    一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是无锡的学生,否则,就不是。如果还想区分出是无锡哪个班的学生,这时候可以为胸牌再增加一个属性来进行区分。

    加了属性的标记效果为:@MyAnnotation(color="red")。

    ·定义基本类型的属性和应用属性:
    在注解类中增加String color(); 被添加的注解设置属性值:@MyAnnotation(color="red")。

    ·用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
    MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
    System.out.println(a.color());
    可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象。

    ·为属性指定缺省值:
    String color() default "yellow";
    value属性:String value() default "zxx";
    如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。

    ·数组类型的属性
    int [] arrayAttr() default {1,2,3}; 被添加的注解设置属性值:@MyAnnotation(arrayAttr={2,3,4})。
    如果数组属性中只有一个元素,这时候属性值部分可以省略大括号。

    ·枚举类型的属性
    EnumTest.TrafficLamp lamp() ; 被添加的注解设置属性值:@MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)。

    ·注解类型的属性:
    MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx"); 被添加的注解设置属性值:@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )
    可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
    MetaAnnotation ma =  myAnnotation.annotationAttr();
    System.out.println(ma.value());

  • 相关阅读:
    Asp.net Treeview 客户端选中效果实现 (初级)
    MYSQL生成日历表,通常在做报表的时候需要用来生成一个临时表,用来左连接等。
    写了一个抽奖类,感觉还不错,可以适合各种变化
    将系统的内部类:HttpValueCollection 移到自己的系统中,使其能方便的解析id=1&name=张三&sex=男这样的字符串参数 querystring
    指定某个文件的创建 修改 访问时间
    Reqeust["keyname"] 的读取顺序
    pku1463 Strategic game
    pku1947 Rebuilding Roads
    pku1848 Tree
    pku1056 IMMEDIATE DECODABILITY
  • 原文地址:https://www.cnblogs.com/driverwjd/p/3902375.html
Copyright © 2020-2023  润新知