java基础知识加强(一)

来源:互联网 发布:复杂网络就业方向 编辑:程序博客网 时间:2024/06/08 03:42

静态导入
1、import语句可以导入一个类和某个包中的所有类
2、import static语句导入一个类中的某个静态方法或所有静态方法
   import static+包名+类名+静态方法者就叫静态导入
可变参数
1、对于可变参数可以用数组的方式进行访问
2、对于可变参数一定要放在最后,否则无法编译
overLoad和overRide的区别?
overLoad重载:这个在函数名一样的情况下,只与参数的列表有关。
overRide复写:这个在函数名一致的情况下,还要保证参数列表一致,一般是子类复写父类,也就是说是出现在继承或者实现中
增强行for循环
语法格式
    for(类型名 变量名:collection集合或数组名)
注意:
    迭代变量必须在()中定义
    集合变量可以是collection集合或数组
基本数据类型自动拆箱和装箱
    例如:
    Integer i=5;//自动装箱实际上是i=Integer.valueOf(5);
    Integer j=6;//自动装箱实际上是j=Integer.valueOf(6);
    Integer a=i+j;//自动拆箱在装箱Integer a=Integer.valueOf(i.intValue()+j.intValue());
注意:
    自动拆箱和装箱只是用于-128~+127其他数不适合
例如:
    Integer a=128;
    Integer b=128;
    那么a==b返回值是false
    当Integer a=127,b=127时
    那么a==b返回值是true
上面的例子告诉我们当数据不在-128~127之间时将会重新开辟新的空间因此他们的==不相等
享元模式:
就是有很多小的对象,他们有很多属性是相同的,把他们变成一个对象,哪些不同的对象不成方法的参数,称之为外部状态,那些相同属性称之为内部状态
为什么要有枚举:
枚举就是要让某个类型的变量只能为若干固定的值中的一个,否则编译就会报错,枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标
用普通类如何实现枚功能:例如定义一个WeekDay的类来模拟枚举功能
1、私有的构造方法
2、每个元素分别用一个公有的静态成员变量表示
3、可以有若干个公有方法或抽象方法,例如要把nextDay方法设为抽象的。
abstract class WeekDay{    private int time;    private WeekDay(){}//定义无参数的构造方法    private WeekDay(int time)//定义一个有参数是构造方法    {        this.time=time;    }    public static final WeekDay SUN=new WeekDay(50){//定义一个带有参数的匿名类,并且实现抽象方法,这模拟了枚举中的带参数的创建匿名类        public WeekDay nextDay()        {            return MON;        }    };    public static final WeekDay MON=new WeekDay(40){        public WeekDay nextDay()        {            return TUE;        }    };    public static final WeekDay TUE=new WeekDay(){        public WeekDay nextDay()        {            return WED;        }    };    public static final WeekDay WED=new WeekDay(){        public WeekDay nextDay()        {            return THU;        }    };    public static final WeekDay THU=new WeekDay(){        public WeekDay nextDay()        {            return FRI;        }    };    public static final WeekDay FRI=new WeekDay(){        public WeekDay nextDay()        {            return SAT;        }    };    public static final WeekDay SAT=new WeekDay(){        public WeekDay nextDay()        {            return SUN;        }    };    public  abstract WeekDay nextDay();/*    public WeekDay nextDay()//没有使用匿名类的写法    {        if(this==SUN)            return MON;        if(this==MON)            return TUE;        if(this==TUE)            return THR;        if(this==THR)            return FRI;        if(this==FRI)            return SAT;        if(this==SAT)            return SUN;        return null;            }*/    public String toString()//覆盖toString方法来实现此模拟的枚举类的打印值    {        if(this==SUN)            return "SUN";        if(this==MON)            return "MON";        if(this==TUE)            return "TUE";        if(this==WED)            return "WED";        if(this==FRI)            return "FRI";        if(this==SAT)            return "SAT";        return null;    }}

定义一个真正的枚举类如下:
public class Test3 {    public static void main(String[] args) {    }    public enum WeekDay{//定义一个WeekDay枚举类        SUN(20){//定义一个带参数的抽象类            public WeekDay nextDay()            {                return MON;            }        },        MON(){//相当于MON            public WeekDay nextDay()            {                return MON;            }        },        TUE{            public WeekDay nextDay()            {                return MON;            }        },        WED{            public WeekDay nextDay()            {                return MON;            }        },        THU{            public WeekDay nextDay()            {                return MON;            }        },        FRI{            public WeekDay nextDay()            {                return MON;            }        },        SAT{            public WeekDay nextDay()            {                return MON;            }        };        private int time;        private WeekDay(int time){this.time=time;}//定义带参数的构造方法        private WeekDay(){}//定义无参数的构造方法        public abstract WeekDay nextDay();//定义nextDay的抽象函数    }}

通过上面的程序可以看出
1、枚举要定义在类中,如果定在类外将会编译失败,其中的每一个成员就相当于一个创建一个枚举对象,只是不像普通类那样需要new。
2、如果枚举中有方法一定要放在数据列表的下面也就是说数据列表要放在开头,并且如果数据列表下没有任何语句那么最后可以不加分号,但是如果有则必须加分号
3、在枚举中其中的SUN()和SUN表示的是一个意思都是调用了无参数的构造方法
知识扩充:当枚举中只有一个参数时就可以作为一种单例实现模式
JDK1.2之后出现的反射;
反射的基石----->Class类
java程序中的各个java类属于同一类事物,描述这类事物的java类就是Class类
获取一个类的字节码的方法有三中
1、类名加上.class//String.class
2、类的对象加上.getClass();//str.getClass();
3、Class.forName("类名");//Class.forName("java.lang.String");
对于上面的三种常用的方法是第三种
原因:
开始不需要加载类,在运行的时候加载类
对于反射可以用getSuperclass来获得对象的父类的字节码,注意class是小写
九个预定义Class实例化对象是八个基本数据类型加上一个void类型
对于这九个预定义Class实例化对象可以用isPrimitive()方法来判断
对于是否为数组可以用isArray()方法进行判断
对于基本数据类型的对象可以用它的TYPE属性反应出他们各自对应基本数据类型的字节码
例如:int.class==Intgeger.TYPE他们返回的值是true
反射的获得构造方法:
对于获得构造方法使用的类是Constructor其中用到的方法有Class类中的getConstructor()(获得单个的构造函数或)getConstructors()(获得所有构造函数)他们返回Constructor类型和Constructor类中newInstance()
还有就是可以用Class类中newInstance()获得一些无参数的构造方法,他们的返回值是Object类型
例如:
    Constructor<String> constructor=String.class.getConstructor(StringBuilder.class);//获取String(StringBuilder)函数
    String str=constructor.newInstance(new StringBuilder("abc"));//同过获得的构造方法来实现所想要的功能
    System.out.println(str.charAt(2));
    Constructor[] constructors= String.class.getConstructors();
    for(Constructor<String> s:constructors)
    {
        System.out.println(s);
    }
通过上面的程序可以看出对于获得单个构造函数可以用泛型确定得到的构造类型,但是对于获得所有构造函数不需要用,记住这个地方就没法用,如果要用泛型就会画蛇添足没什么用处
对于私有的构造函可以用getDeclaredConstructor来获取无权限的,然后将获得的Constructor的对象的setAccessible设置为true就可以了这就叫做暴力获取
反射获取类中的成员变量:
对于获得成员变量可以用Class类中的getField()方法和getFields()方法分别获得单个变量和全部变量,他们的返回值都是Field的类型的,然后在通过Field类中的get获得变量的值,然后可以有set方法对变量只进行修改
如果操作无权限也可以使用暴力获取,用到的方法有Class类中getDeclaredField()或者getDeclaredFields()还有Field类中的setAccessible方法实现可操作权限,然后就于一般的取值和修改一样了
练习题:
将一个任意对象中的String类型的成员函数中的b字符都换成a字符:
class FanShe3{    public void run() throws Exception    {        LiZi lz=new LiZi();        Field[] fields=LiZi.class.getDeclaredFields();//        fields.setAccessible(true);//这个是操作单个成员变量使用的,前提上面要改成Field fields=LiZi.class.getDeclaredField('s1');//        String oldS1=(String)fields.get(lz);//这是为了表明要获得那个对象上的s1成员变量//        String s2=oldS1.replace('a', 'b');//        fields.set(lz, s2);        for(int i=0;i<fields.length;i++)        {            if(fields[i].getType()==String.class)            {                fields[i].setAccessible(true);//将所有权限都修改为可操作                String oldValue=(String)fields[i].get(lz);                String newValue=oldValue.replace('b', 'a');                fields[i].set(lz, newValue);            }        }        System.out.println(lz);    }}


反射获取类中的成员函数:
用到的方法是Class类中getMethod()或getMethods()如果获取搜有权限的用getDeclaredMethod()或getDeclaredMethods()对于湖区所有权限的有用到setAccessible来设置成可操作,然后就是用获得的Method对象
调用invoke()方法,类实现反射调用类中的方法
代码演示:
class FanShe4{    public void run() throws Exception    {        LiZi lz=new LiZi();//创建一个对象        Method method=LiZi.class.getMethod("print", int.class,char.class);//获取类中的print相对应的方法        method.invoke(lz, 23333,'a');//先表明那个对象,以及传入相应的参数值        Method[] methods=LiZi.class.getMethods();//实现获得此类中所有方法,以及继承的方法        for(Method me:methods)        {            System.out.println(me);        }    }}

对于上面的method.invoke(lz,233,'a');如果lz变成null那么将是调用的是静态方法
用发射的方式执行某个类中的main方法
目标:写一个类能够很据用户提供的类名去执行类中的main方法
public class FanShe {    public static void main(String[] args) throws Exception {        String className=args[0];//参数中传过来的类名        Method method=Class.forName(className).getMethod("main", String[].class);//获得此类中的main方法        method.invoke(null,(Object)new String[]{"1111","222","222"});//前面加Object是为了防止程序将其数组拆包而导致异常发生,从而无法编译成功    }}

对于上面还有一种处理方法就是将数组包裹到Object类型中例如new Object[]{new String[]{"1111","222","222"}};这样也是可以的
对于上面为什么要将其进行转化而不是单独写上new String{"11","22","33"}原因是在jdk1.5中他会被看成是一个整体,但是在jdk1.4中数组每一个元素会被看成一个参数,而且高版本都是向下兼容的
main函数中传递的是数组参数所以程序会运行jdk1.4的语法导致数组被打散,从而出现异常导致无法编译通过,所以一定要将数组变成一个整体让她不要打散即可,上面的两种方法就是防止编译时将数组打散
数组的反射:
数组中的一些特点:
1、居于相同维数和元素类型的数组属于同一个类型,既具有相同的class实例对象
2、代表数组的class实例对象的getSuperClass()方法返回的父类的Object类对应的class
3、基本类型的一位数组可以被当做Object类型使用,不能当做Object[]类型使用,非基本类型的一位数组,既可以当做Object类也可以当做Object[]类,因为基本数据类型不是Object对象
对于三如下例子:
int[] a1=new int[3];
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[3];
对于上面的语句
a1.getClass()==a2.getClass();返回值是true
a1.getClass()==a3.getClass();返回值是false
a1.getClass()==a4.getClass();返回值是false
对于后两个语句在高版本中无法进行比较会出现错误
对于数组可以用Arrays类中asList将数组转变成List集合,但对于基本数据的数组只会将数组的对象引用地址给转化到List集合中,但对于引用型数组可以将数组中的元素转化到List集合中

对于获取数组中的某个元素或修改某个位置的元素可以用Array类中get(数组名,index)和set(数组名,index,现在值)方法来实现

用反射的方法实现ArrayList

1、先建立一个配置文件config.properties

在里面写上代码:

classpath=ArrayList

2、引入配置文件

        FileReader fr=new FileReader("config.properties"); Properties props=new Properties(); props.load(fr); fr.close();        String className=props.getProperty("className");//得到类的名字        Collection collection=(Collection)Class.forName(className).newInstance();//创建Collection集合对象
或者

          InputStream is= FanShe1.class.getClassLoader().getResourceAsStream("bao/config.properties");  Properties props=new Properties();  props.load(is);  is.close();          String className=props.getProperty("className");                    Collection collection=(Collection)Class.forName(className).newInstance();//创建Collection集合对象

加载类:类加载器是负责加载类的对象。ClassLoader类是个抽象类。如果给定类的二进制名称,那么类加载器会视图查找或生成构成类定义的数据。一般策略是将名称转化为某个文件名,然后从文件系统读取该名称的类文件

内省:

在很多情况下,要对类中属性进行频繁操作,而反射操作比较复杂,因此Sun公司有开发了专门处理成员变量的JavaBean类,来对类中的成员变量进行操作。

通过内省获得类中属性有两种方法,

1、用PropertyDescriptor获取它的对象然后通过这个对象得到某个属性的getReadMethod或getWriteMethod方法然后通过反射获得此属性相对应的get和set方法

代码为:

Person p=new Person("zjd",24);                PropertyDescriptor pd=new PropertyDescriptor(propertyName,p.getClass());//其中propertyName指的是传入的属性Method methodGet=pd.getReadMethod();System.out.println(methodGet.invoke(p));//通过反射得到属性的值Method methodSet=pd.getWriteMethod();methodSet.invoke(p, "zxm");//通过反射设定属性的值System.out.println(methodGet.invoke(p));
2、先创建一个BeanInfo对象,通过他的对象的getPropertyDescriptors方法得到PropertyDescriptor对象数组,然后遍历此数组中的成员名找到相对应的属性名,然后接下来就和第一种方法一样了

代码如下:

BeanInfo beanInfo=Introspector.getBeanInfo(p.getClass());//创建BeanInfo对象PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();//此方法只能获取所有的属性名,而不能获取单个的属性名for(PropertyDescriptor pd:pds)//通过for的强类型进行遍历数组{if(pd.getName().equals("name"))//判断是否为name属性{Method methodGet=pd.getReadMethod();System.out.println(methodGet.invoke(p));}}
通过上面的两中方式都要用到反射说明内省一般都是和反射放在一块用的,因为需要获得操作属性的放法

对于JavaBean的操作apache公司还提供了BeanUtils类供我们使用例如上面的两种方法可以用BeanUtils类中的方法实现,代码如下:

System.out.println(BeanUtils.getProperty(p, "name"));//获取name的值,这个数据类型返回值是String,即使他的属性是别的也是StringBeanUtils.setProperty(p, "name", "zxm");//设置name的值System.out.println(BeanUtils.getProperty(p, "name"));
对于这中方法setProperty最后一个参数一定是String类型他会将数据自动转化为相对应的数据类型

还有一种方法需要指明数据类型例如

System.out.println(PropertyUtils.getProperty(p, "age"));//这个属性的类型是age本身的数据类型PropertyUtils.setProperty(p, "age", 33);//这个地方要传入和相对应的数据类型一致的System.out.println(PropertyUtils.getProperty(p, "age"));
在使用BeanUtils包的时候一定要记得导入logging日记文件否则编译无法通过,还有要将这两个先放在lib目录先让后导入到Build Path中去。

对于java1.7的新特性

Map map={name:"zjd",age:23}

BeanUtils.setProperty(map,"name","zxm");

也是正确的