黑马程序员_Java基础加强_jdk1.5重要新特性详解

本文深入讲解Java 1.5引入的重要新特性,包括静态导入、可变参数、增强的for循环、基本数据类型的自动装箱与拆箱、享元模式、枚举、泛型等内容,帮助读者更好地理解和运用这些新特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,静态导入:
    import语句是导入包中的某个类,或者包中的所有类,它不占用任何资源,只是让程序员们少写一些包名和
类名。
    import static 语句是导入一个包中的某个静态方法或者所有静态方法。比如说Math类,Arrays类,
    Collections类,导入这些类之后,就可以不用写这些类名了,可以直接写方法。
    import static Math.max;在使用max方法时就可以省略Math类名了。

二,可变参数:
    当某个方法要接受若干个参数,就可以使用可变参数。比如计算某几个数的和,没有可变参数之前,只能通过多个重载来实现。
    override(重写):当父类的某个方法是私有的,在子类中有一个和父类该私有方法一模一样的方法,那么子类的这个方法不是重写的父类的方法,而是一个全新的方法。
    overload(重载):两个方法名字相同,参数列表相同(个数和类型都相同),但是方法返回值类型不同,那么这种情况编译会出错。不构成重载。当参数个数相同,但是顺序不同,也属于重载,这种情况属于参数类型不同。
   注意: 可变参数只能在参数列表的最后面。(String name,int...args)。方法体内是以数组的方式访问这些参数的。

三,增强的for循环:
    for(类型 变量名 :集合变量名){...}

四,基本数据类型的自动装箱与拆箱:
    Integer iobj = 15;-->自动装箱,15是int型的基本数据类型,使用该语句会自动将15装箱成一个Integer对象。
    System.out.println(iobj + 20);-->自动拆箱,将iobj对象拆箱成基本数据类型然后与20相加。
注意:自动装箱与拆箱只是在基本数据类型与基本数据类型对应的引用类型之间。一共八种基本数据类型,所以自动拆箱与装箱也就八种情况。
 
示例:
    Integer i1= 3;
    Integer i2= 3;
    System.out.println(i1 == i2);//true
    当将同一个int类型的数通过自动装箱封装成两个Integer对象后,如果这个整数小于一个字节(-128-127之间),那么在内存中这两个Integer对象是同一个对象。原因是当将-128-127之间的整数封装成对象后,java会把内存中这个对象缓存起来,放到缓存池,当下一次再要使用该整数对象的时候,就不需要重新在新建一个一样对象了,可以直接拿来用,就会节省内存资源。这种模式也叫享元模式。

享元模式(flywight):当有很多个小的对象,他们有很多相同属性,那么就把他们变成一对象,缓存起来,当下次再要使用到该对象的时候就不用。在内存中重写创建新的对象,而直接从缓存中拿出来使用,这样可以节约资源,这种模式成为享元模式。

五,枚举:
    枚举就是要让某个类型变量的取值只能是若干个固定值中的一个,否则编译器就会报错。比如,规定将Sunday等于0,为了防止有人写成7,就可以使用枚举,如果别人写成7,那么编译时就会出错。作用就是:可以让编译器在编译时就控制远程序中填写的非法值,普通变量的方式在开发阶段是无法实现这一目标的。

枚举特点:
    (1)枚举不能使用extends关键字,但是可以使用implement关键字,这样我们就可以把不同枚举类的公有行为提取到接口中规范枚举的行为。
    (2)永远不能直接调用枚举的构造函数,通过枚举元素的变元可以调用相应的构造函数,自定义构造函数不能覆盖默认执行的构造函数,它会在默认构造函数之后执行。枚举的构造方法可以重载。
    (3)枚举的构造函数都是私有的。
    (4)枚举中的元素定义必须放在最上面,最后才能是变量和方法的定义。
    (5)枚举中的元素,也就是常量,默认是public static final的,所以要大写,但是不能显示的写出,否则编译出错。
    (6)创建一个枚举类:enum{};后面的括号可有可无,不写编译器不会报错。


枚举是1.5出现的,之前如果要实现枚举的功能,是通过普通类来实现的。
需求:使用一个普通类模拟枚举的原理。定义一个WeekDay类,他有一个nextDay()方法,调用该对象的该方法可以获取下一天的星期数。

abstract class WeekDay1 {
    private WeekDay1() {}
    //定义一个静态常量,常量的类型是该常量所在类的类型。常量值是该类的子类实例对象
    public final static WeekDay1 SUN = new WeekDay1() {
        public WeekDay1 nextDay() {
            return MON;
        }
        //重写的toString方法
        public String toString() {
            return "SUN";
        }
    };
    
    public final static WeekDay1 MON = new WeekDay1() {
        public WeekDay1 nextDay() {
            return TUE;
        }
        
        public String toString() {
            return "MON";
        }
    };
    
    
    public final static WeekDay1 TUE = new WeekDay1() {
        public WeekDay1 nextDay() {
            return SUN;
        }
        public String toString() {
            return "TUE";
        }
    };
    //抽象类的抽象方法
    public abstract WeekDay1 nextDay();
    
    //这里我们可以采用抽象方法定义nextDay(),就可以将大量的if else语句转化成一个个独立的对象
}
public class EnumTest
{
    public static void main(String[] args) {
        WeekDay1 w = WeekDay1.SUN;
        System.out.println(w + "::" + w.nextDay());//结果是:SUN::MON
    }
}

枚举的定义:

public enum WeekDay {
    SUN,MON(1),TUE(2,3),WEN(1,2,3),THI(),FRI,SAT;
    
    //构造方法
    private WeekDay() {System.out.println("first");}
    private WeekDay(int day) {System.out.println("Second");}
    private WeekDay(int day,int day2) {System.out.println("third");}
    private WeekDay(int day,int day2,int day3) {System.out.println("Four");}
}

定义枚举时要注意:
        (1),枚举也是一个类,枚举中的元素(SUN,MON,TUE...)都是该类的实例对象。
        (2),枚举的构造函数,成员变量和成员函数必须放在元素列表(SUN,MON,TUE...)的后面。
        (3),在枚举元素后面添加括号,通过参数列表的不同可以让不同的元素调用不同的构造函数。
        (4),当枚举的成员没有添加括号或者只有空括号时,该元素默认调用的是枚举无参数的构造方法。
        (5),如果枚举中存在了带参数的构造方法,又有无括号的元素那么枚举中必须要有无参数的构造方法,因为无括号的元素默认调用无参数的构造方法。

枚举的实际应用:
需求:定义一个交通灯的枚举,交通灯有三种颜色,每次灯亮有一定的时间。

public enum TrafficLight {
    RED(45) {
        public TrafficLight nextLight() {
            return GREEN;
        }
    },
    GREEN(30) {
        public TrafficLight nextLight() {
            return YELLOW;
        }
    },
    YELLOW(15) {
        public TrafficLight nextLight() {
            return RED;
        }
    };
    public abstract TrafficLight nextLight();
    private int time;
    private TrafficLight(int time) {
        this.time = time;
    }
}

补充:
        (1),当一个枚举在一个类内部时(也就是内部类,因为枚举也是一个类),可以使用四个访问修饰符修饰,因为内部类也是类的成员,和类中的成员是一级的。外部类只有两种访问修饰符,public和default(默认的)。
        (2),当枚举只有一个成员的时候,就是一个单例设计模式。所以当定义单例设计模式的时候,可以使用枚举来定义。
 

6,泛型
        jdk1.5出现的泛型是新特性当中最难的一个,也是十分重要的一个新特性。
        如果不出现泛型,那么任何一个任何类型对象,都可以存储到集合中,而出现泛型以后就将集合中的元素限定为一种特定
类型,这样更安全,当从集合中取出元素的时候,程序也知道取出的元素是什么类型,这样就不需要将元素进行强制类型转换了。
如果不使用泛型,那么编译器就会给出一个警告unchecked。

    泛型是给编译器使用的,可以限定集合中存储的元素的类型,挡住用户向集合中输入泛型以外的类型,当程序运行时,这些泛型信息
就没有了。也就是使用getClass()得到的字节码文件里面是没有泛型信息的。可以使用下面的程序检测。
    ArrayList<String> list1 = new ArrayList<String>();
    list1.add("abc");
    ArrayList<Integer> list2 = new ArrayList<Integer>();
    list2.add(99);
    System.out.println(list1.getClass() == list2.getClass());//结果是true,说明这两个集合指向的是同一份字节码文件

通过上述原理我们就可以知道,只要跳过编译器,即使是在编译期间定义了集合的泛型,在运行期间还是可以加入泛型以外的
元素的。
方法:
    ArrayList<Integer> list2 = new ArrayList<Integer>();
    list2.add(99);
    //使用反射调用list2的add方法,注意第二个参数是Object.class
    list2.getClass().getMethod("add", Object.class).invoke(list2, "defghijkl");
    System.out.println(list2);//[99, defghijkl]

使用泛型要注意的几点:
    <1>,ArrayList<E>和ArrayList<Integer>的几个重要术语:
        (1)ArrayList<E>称为泛型类型
        (2)ArrayList<E>中的E称为类型变量或类型参数
        (3)ArrayList<Integer>称为参数化的类型
        (4)ArrayList<Integer>中的Integer称为类型参数的示例,或参数的实际类型
        (5)ArrayList称为原始类型

    <2>,参数化类型与原始类型的兼容性:
        (1)参数化类型可以引用一个原始类型:Collection<String> c = new Vector();
        (2)原始类型可以引用一个参数化类型的对象:Collection  c = new Vector<String>();
        这两中形式都有警告。

    <3>,参数化类型是不考虑类型参数的继承关系:
        Vector<String> v = new Vector<Object>();//错误
        Vector<Object> v = new Vector<>(String);//错误

    <4>,创建数组实例时,数组元素不能使用参数化的类型:Vector<String> v[] = new Vector<String>()[];//错误

    <5>,思考:Vector v = new Vector<String>();
        Vector<Object> v2 = v;
        这个在编译时是不会报错的,原因就是因为泛型是给编译器使用的,编译器在编译代码时是一行一行扫描的,从注意的第二点可以
看到这两种形式都是正确的。运行时,才是执行代码,不能以执行代码的方式看着写代码。


    泛型通配符的使用:
        因为泛型不存在继承关系,所以当一个方法接收的参数是一个泛型类型的集合应该怎么做呢?这时候就要用到通配符?
        public void printCollection(Collection<?> coll);
        该方法表示接受的Collection类型的参数是任意类型,不能使用Collection<Object> coll,这是错误的。
        coll就可以接受Collection<String>,Cololection<Integer>...

        只要是使用了泛型通配符的集合,?就可以引用任意类型的参数化类型,可以调用与参数化无关的方法,而不能调用与参数化有关的方法,比如Collection的add()方法是不能调用的,但是size()方法可以调用,因为与参数类型无关。

public void printCollection(Collection<?> coll) {
    coll.add();//错误
    coll.size();//正确
    coll = new HashSet<Date>();//正确
}
public void printCollection(Collection<Object> coll) {
    coll.add();//正确
    coll = new HashSet<String>();//错误,泛型不存在继承
}
 
     泛型通配符的扩展:
    限定通配符的上边界:
    Vector<? extends Number> v = new Vector<Integer>();
    限定通配符的下边界:
    Vector<? super Integer> v = new Vector<Number>();

泛型的一个应用:对Map集合中的元素进行迭代:

Map<String,Integer> map = new HashMap<>();
map.put("abc",20);
map.put("def",21);
map.put("ghi",22);
for(Map.Entry<String,Integer> entry : map.entrySet) {
    String key = entry.getKey();
    int value = entry.getValue();
}

最简单的自定义泛型的使用:
        除了在使用方法时会出现泛型,通配符,限界等,还可以自定义泛型,使用通配符。在自定义方法上定义泛型时,泛型类型T要在返回值类型前面并且是紧挨着返回类型的说明这种类型。比如自定义一个交换数组元素位置的方法:

public static <T> void swap(T[] arr,int i,int j) {
    T temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

这里要注意要说明T类型,在void前,紧挨着void的。T可以使任意类型,arr是任意类型的数组,交换arr数组的i和j位置的元素。
    普通方法,构造方法和静态方法都可以使用泛型。

    泛型中可以同时有多个参数,泛型的参数之间用逗号隔开,比如Map集合,Map<K,V>.也可以使用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。

private <? extends Exception> void sayHello() throws T {
    try
    {
        
    }
    catch (Exception e)
    {
        throw (T)e;
    }
}

泛型的练习:

//将Object类型的对象转换成其他类型
    public static <T> T swapObject(Object obj) {
        return (T)obj;
    }
    
    //将任意类型的数组中的所有元素填充为相应类型的某个对象
    public static <V> void fill(V[] a,V obj) {
        for(int i=0;i<a.length;i++) {
            a[i] = obj;
        }
    }
    
    //打印出任意参数化类型的集合中的所有元素
    public static <T> void print2(Collection<T> coll) {
        for(T a : coll) {
            System.out.println(a);
        }
    }
    //把任意参数类型的集合中的数据安全的复制到相应类型的数组中
    public static <T> void copy(Collection<T> coll,T[] arr) {
        int i = 0;
        Iterator<T> it = coll.iterator();
        while(it.hasNext()) {
            arr[i] = it.next();
            i++;
        }
    }
    //把任意参数类型的一个数组中的数据安全的复制到相应类型的另一个数组中
    public static <T> void copy2(T[] src,T[] desk) {
        for(int i=0;i<src.length();i++) {
            deski[i] = src[i];
        }
    }

定义类级别的泛型:
    当类中的多个方法使用到了同一个泛型类型,这时候可以将泛型定义成类类型的泛型。
    定义类类型的泛型还要注意,当类中要定义静态方法时,静态方法是不能使用类类型的泛型的,但是可以单独给静态方法定义一个泛型,相互之间不影响。如果泛型定义在方法上时,多个方法的泛型之间是相互独立的。比如上面的示例中定义的泛型,相互之间的T是
各不相同的。

    对数据库的操作其实就是对数据库的数据进行增删改查的,专业术语是:CRUD
    当看到要定义一个Dao类时,就说明是要定义一个CRUD的类,DAO--->Data Access Object
    定义Dao就是定义一个包含CRUD的类。

一个简单的Dao,包括CRUD。

public class GenericDao<T> {
    public void add(Object obj) {
        
    }
    public T findId(int id)  {
        return null;
    }
    public void delete(T obj) {
        
    }
    public void delete(int id) {
        
    }
    public void update(T obj) {
        
    }
    //这里的E类型是和类的泛型是没关系的,和类泛型同名时不冲突的
    public static <T> void update2(T obj) {
        
    }
    public Set<T> findByCondition(String where) {
        return null;
    }
    public T findByUesrName(String name) {
        return null;
    }
}

泛型的高级应用:通过反射获取泛型实际参数类型
        比如:public static void void appolyPara(Vector<Date> v):
        该怎样通过反射获取Vector的泛型类型呢?

public class GenericTest2 {
    public static void main(String[] args) throws Exception  {
        //使用反射获取方法
        Method genMethod = GenericTest2.class.getMethod("applyPara", Vector.class);
        //获取泛型参数的实际类型参数列表
        Type[] types = genMethod.getGenericParameterTypes();
        //因为参数类型列表只有一个元素,直接获取
        ParameterizedType pType = (ParameterizedType) types[0];
        System.out.println(pType.getRawType());//打印方法的参数类型
        System.out.println(pType.getActualTypeArguments()[0]);//打印方法参数的泛型类型
    }
    //要获取的方法
    public static void applyPara(Vector<Date> v) {
    }
}

 结果:
class java.util.Vector
class java.util.Date 





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值