首页 > 代码库 > 黑马程序员_Java高新技术

黑马程序员_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=http://www.mamicode.com/部分,例如:@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());