Java新特性

可变参数

早期如果想让一个方法可以接收任意数量的参数,实现方式是把多个数据封装为一个数组。而有了可变参数Java会自动把传递的多个参数封装成数组,使用起来就方便多了。

可变参数定义格式

修饰符 返回值类型 方法名(数据类型… 变量名) {  }

可变参数的注意事项

  • 这里的变量其实是一个数组
  • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
public class VariableParas {
    public static void main(String[] args) {
        System.out.println(sum(10,20));
        System.out.println(sum(new int[]{1,2,3}));
        System.out.println(sum(1,2,3));
    }

    public static int sum(int... data){ //可变参数
        int sum = 0;
        for(int i : data){  //JVM会自动把多个参数封装成数组
            sum += data[i];
        }
        return sum;
    }
}
/*执行结果:
30
6
6
*/

增强for循环

增强for循环用来简化读取数组元素的代码,非常好用。

public class TestDemo{
    public static void main(String[] args){
        int[] ints = new int[]{1,2,3,4,5};
        for(int x: ints){
            System.out.println(x);
        }
    }
}

泛型

对象的向上转型解决了方法参数类型的统一,但是向下转型可能会存在参数类型转换异常(ClassCastException),所以向下转型的操作并不安全。为了解决向下转型的安全问题,java提供了泛型技术。

加入要设计一个坐标系,可以使用整数或浮点数当作坐标,也可以用字符串描述坐标,那么为了实现参数的统一,使用所有对象的父类,Object类作为属性类型。

class Point{
    private Object x;	//可以保存任意数据
    private Object y;	//可以保存任意数据
    public void setX(Object x){
        this.x = x;
    }
    public void setY(Object y){
        this.y = y;
    }
    public Object getX(){
        return x;	//注意这里返回的是对象
    }
    public Object getY(){
        return y;
    }
}

public class TestDemo{
    public static void main(String[] args){
        Point pa = new Point();
        pa.setX(10.2);
        pa.setY(21.2);
        double x = (Double)pa.getX();//getX()方法返回一个Object对象,通过向下转型变为Double对象,再通过自动拆箱取出值赋值给一个double变量
        double y = (Double)pa.getY();
        System.out.println("坐标为:("+x+","+y+")");
        
        Point pb = new Point();
        pb.setX("东经123度");
        pb.setY("北纬34度");
        String bx = (String)pb.getX();//这里向下转型只有在知道了是String类型时才可以,否则很容易出错
        String by = (String)pb.getY();
        System.out.println("坐标为:("+bx+","+by+")");
    }
}
/*执行结果:
坐标为:(10.2,21.2)
坐标为:(东经123度,北纬34度)
*/

Object属性可以接收任意类型的对象,这样在编译时不会出错。但在实际运行时可以会出现参数类型不匹配的问题。下列代码在接收数据时传入的浮点数,通过向上转型变为Object不会出错。但在将数据取出时会强制转换为String类型对象,就发生了ClassCastException异常。

public class TestDemo{
    public static void main(String[] args){
        Point pa = new Point();
        pa.setX(10.2);
        pa.setY(21.2);
        String x = (String)pa.getX();//double类型的不能向下转型成为String对象
        String y = (String)pa.getY();
        System.out.println("坐标为:("+x+","+y+")");
    }
}
/*执行结果:
Exception in thread "main" java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.String (java.lang.Double and java.lang.String are in module java.base of loader 'bootstrap')
	at Test.main(Test.java:23)
*/

JDK1.5引入了泛型技术,此技术的核心在于:类属性或方法的参数在定义数据类型时,可以直接使用一个标记进行占位,在具体使用时才设置其实际数据类型,这样当设置的数据类型出错时,就可以在程序编译时检测出来。

//使用泛型的方式重写Point类
class Point<T>{     //T是type的简写,是一个类型标记
    private T x;    //此属性的类型不知道,在使用时动态决定
    private T y;
    public void setX(T x){
        this.x = x;
    }
    public void setY(T y){
        this.y = y;
    }
    public T getX(){
        return x;
    }
    public T getY(){
        return y;
    }
}
public class NewTest {
    public static void main(String[] args) {
        Point<String> p = new Point<String>();
        p.setX("东经123度");
        p.setY("北纬32度");
        String x = p.getX();
        String y = p.getY();
        System.out.println("坐标为:("+x+","+y+")");

    }
}
//执行结果:坐标为:(东经123度,北纬32度)

使用了泛型后,所有类中属性都是动态设置的,避免了向下转型的安全问题,这样的操作才属于安全的操作。不过泛型可以采用的类型必须是引用类型,不可以使用基本数据类型。注意,在一个类上可能会定义多种泛型声明。下面的代码除了定义参数类型外,还定义了返回值类型。

泛型定义格式

  • <类型>:指定一种类型的格式。这里的类型可以看成是形参
  • <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
  • 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
  • 注意:泛型标记加在类名和接口名后,但是加在方法名前的修饰符位置

泛型的好处

  • 把运行时期的问题提前到了编译期间
  • 避免了强制类型转换

泛型类、泛型接口

泛型类的定义

//泛型类定义格式: 修饰符 class 类名<类型> {}
class Point<E,T>{
    public R fun(E e){
	return null;	
    }
}

测试:

package GenericTest;

class Student<T> {
    private T t;
    public Student(T t){
        this.t = t;
    }
    public void show(){
        System.out.println(t);
    }
}

public class GenericClassTest{
    public static void main(String[] args) {
        Student<String> s1 = new Student<String>("风清扬");//jdk1.7后不用写后面的String了
        Student<Double> s2 = new Student<Double>(3.14);
        Student<Integer> s3 = new Student<Integer>(20);
        student<> s4 = new Student<("hello");//如果实例化时不指定类型,默认是Object类型

        s1.show();
        s2.show();
        s3.show();
        s4.show();
    }
}

泛型类的继承

  1. 父类指定泛型的类型,子类就不是泛型类了

    public class<E> fu{//父类是泛型类
        public void method(E e){}
    }
    
    public class Zi extends Fu<Integer>{//父类指定了Integer类型,子类就被限定成了Integer类型,不用再写泛型标记了
       
    }
    
  2. 父类不指定泛型类型,子类必须是泛型类,类型在使用时确定

    public class Zi<E> extends Fu<E>{
        
    }
    

泛型类注意事项

  1. 泛型类可以定义多个类型参数

    public class TestGenerics<A,B,C>{
        A name;
        B age;
        public C method(A a){}
    }
    
  2. 泛型类的构造器不能加泛型标记

    public TestGenerics<A,B,C>{}//错误,构造器不能加泛型
    public TestGenerics(){}//正确
    
  3. 泛型类中的静态方法不能使用泛型

    public class TestGenerics<E e>{
        public static method<E e>{}//错误,因为静态方法在实例化之前就存在,而泛型的类型是在创建对象时才确定
        public static method2(){}//正确
    }
    
  4. 不能直接创建泛型数组

    public class TestGenerics<E e>{
        E[] array = new E[];//错误
        E[] array = (E[])new Object[];//正确,可以先创建Object数组,再向下转型
    }
    
  5. 泛型没有继承的概念(不能进行多态操作)

    Object obj = new Object();
    String str = new String();
    obj = str;//多态操作,可行
    
    List<Object> objList = new ArrayList<>();
    List<String> strList = new ArrayList<>();
    objList = strList;//错误,指定了泛型就没有继承关系了,不能再转型了
    

泛型接口

如果把接口定义为泛型,那么他的实现类也必须是泛型类。

定义格式

修饰符 interface 接口名<类型> {  }

示例代码

public interface Generic<T> {
    void show(T t);
}

泛型接口实现类

public class GenericImpl<T> implements Generic<T> {//泛型的接口名和类名后面都要加上<T>
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

测试类

public class GenericDemo {
    public static void main(String[] args) {
        Generic<String> g1 = new GenericImpl<String>();
        g1.show("林青霞");

        Generic<Integer> g2 = new GenericImpl<Integer>();
        g2.show(30);
    }
}

泛型方法

定义格式

修饰符 <类型> 返回值类型 方法名(类型 变量名) {  }

示例:带有泛型方法的类。泛型方法不一定是在泛型类中。

package GenericTest;

class Teacher{
    public <T> void show(T t){
        System.out.println(t);
    }
}

public class GenericMethodTest {
    public static void main(String[] args) {
        show("风清扬");
        show(123);
        show(3.14);
    }
}

泛型参数:类型通配符

如果声明了泛型,但在使用时没有明确声明类型,那么JVM默认采用Object类型。虽然泛型实现了不同类型参数的传递问题,但是当实例化对象之后,泛型的类型就确定了,这时就不能直接进行引用操作了。

//下面这样写法不对,不会被java识别为重载,而是重复定义
public void a(List<Object> list){}
public void a(List<String> list){}
public void a(List<Integer> list){} 	

上面代码中的a()方法只能接收一种类型的参数,而且这里不能用方法重载来改写。因为Java中的方法重载只要求参数类型不同,对于泛型类型没有任何要求,所以无法通过方法重载来解决此类问题。
那么能否通过将a()的参数类型声明为List

posted @ 2021-08-29 11:11  黄了的韭菜  阅读(39)  评论(0编辑  收藏  举报