Java中的包装类、泛型

目录

包装类

包装类的产生

JDK中的包装类——以下8种

包装类的使用

包装类与基本类型的不同点

泛型

引入泛型——编译阶段检查类型是否一致的手段。 

若泛型类中存在多个类型参数,成员变量的类型不一定一致。 

泛型方法

泛型声明在接口中

通配符 ?

泛型上限

泛型下限

类型擦除


包装类

包装类:就是把8大基本类型封装到类之中。 

包装类的产生

a. Object类可以接收所有引用数据类型(数组,类,接口)。为了让Object类可以接收Java中一切内容,引入包装类,把基本类型的数值封装到类的对象之中就产生了包装类。


b. 基本类型的默认值其实在很多场景下会造成误导,而引用类型的默认值就是null(可以明确知道此时的值为空)。
例如:有一个扣费的需求,当前费用 * 当前费率(打几折) = 实际扣款数。

如果费率使用double类型,默认值0.0(有歧义,免费场景下也为0.0)。
使用的是double的包装类Double,默认值就是null,避免歧义。

JDK中的包装类——以下8种

包装类的使用

以整型为例

    public static void main(String[] args) {
        int val = 10;
        //int -> Integer
        Integer i1 = new Integer(val);  //装箱
        Object obj = i1;
        // 需要进行数学运算,就需要把包装类的对象还原为基本类型
        //Integer -> int
        int ret = i1.intValue();    //拆箱
        ret += 20;
        System.out.println(ret);
    }
//输出结果
30

装箱:将基本类型的数值保存到包装类对象中。
拆箱:将包装类对象中的数值还原为基本类型。

自动拆装箱——>Java编译器的优化,使用包装类就和使用基本类型一模一样。

    public static void main(String[] args) {
        // 自动装箱
        Integer i2 = 10;
        // 自动拆箱
        i2 += 20;
        System.out.println(i2);
    }
//输出结果
30

包装类与基本类型的不同点

有了自动拆装箱,使用包装类就和使用基本类型完全—致。
除了2个地方不同:

a. 默认值不同。包装类的默认值都是null,基本类型的默认值就是其数据类型默认值。Integer,Double ——> null
int ——> 0
double ——> 0.0
b. 比较相等,仍然使用equals方法比较,所有类对象的比较都使用equals方法!


问题:求输出结果?

    public static void main(String[] args) {
        Integer i1 = 130;
        Integer i2 = 130;
        System.out.println(i1 == i2);
        i1 = 120;
        i2 = 120;
        System.out.println(i1 == i2);
    }

答:同字符串常量池。
当使用整型包装类的自动拆装箱时,JVM会缓存相应的数值。Integer常量池,默认在[-128,127]之间的取值,都会缓存到常量池中。

结论:只要是引用类型的比较都一律使用equals方法!

阿里编码规范:所有POJO(普通)类的成员变量—律使用包装类替代基本类型。

泛型

Java是强类型语言,在定义x和y的时候必须强制定义变量类型。

定义泛型对象时,只能使用类。泛型只能接收引用数据类型,基本类型不能保存到泛型中,必须使用包装类。

List<Integer> list = new ArrayList<>();

Point {
    x;
    y;
}
x = 10,y = 20;
x = 10.1,y = 20.2;
x= "东经15度",y = "北纬39度";
package generic_test;

public class Point {
    private Object x;
    private Object y;
    public Point(Object x, Object y) {
        this.x = x;
        this.y = y;
    }
    public Point() {
    }
    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 static void main(String[] args) {
        Point point1 = new Point();
        point1.setX(10);
        point1.setY(20);
        int x = (int) point1.getX();
        int y = (int) point1.getY();
        System.out.println("x = " + x + ", y = " + y);
    }
}
//输出结果
x = 10, y = 20

当用户不小心输入的x和y是不同类型时,编译是没问题的,但是在下面取出x和y的值时,强制类型转换就会报错,运行时异常。

—般来说,我们要求把所有的错误都提前暴露在编译阶段,程序还没跑起来就能发现错误。

引入泛型——编译阶段检查类型是否一致的手段。 

泛型指的是在类定义时不明确类型,而在使用时明确类型。定义泛型使用"<>"操作符。

泛型类

class MyPoint<T> {
    T x;
    T y;
}

<T> 称之为类型参数,可以使用任何字符,用中文都行,规范都是单个的大写字母。

T:表示任意类型
K:键值对,key值

V:value值
E:单个元素

public class MyPoint<T> {
    // 此时x的类型不定,在产生这个对象时确定类型
    private T x;
    private T y;
}
x和y这两个成员变量的类型不定,在产生MyPoint类的对象时明确x和y的类型。
public class MyPoint<T,E> {
     T x;
     E y;
}
//产生对象
MyClass<Integer,String> cls = new MyClass<>();
package generic_test;

public class MyPoint<T> {
    // 此时x的类型不定,在产生这个对象时确定类型
    private T x;
    private T y;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }

    public static void main(String[] args) {
        // 此时设置的类型就是字符串
        MyPoint<String> point = new MyPoint<>();
        point.setX("北纬10度");
        point.setY("东经20度");
        String x = point.getX();
        String y = point.getY();
        System.out.println("x = " + x + ", y = " + y);
    }
}
//输出结果
x = 北纬10度, y = 东经20度

引入泛型后,可以在编译阶段检查设置的类型值是否是指定类型,若不一致,编译报错。

取出值的时候,避免了向下转型中强制类型转换的出错问题。

可以使用泛型来改造我们动态数组和链表,之前保存的只能是int,当有泛型以后,我们可以保存任意类型。

若泛型类中存在多个类型参数,成员变量的类型不一定一致。 

public class MyPointNew<T,E> {
    private T x;
    private E y;
}

使用不同的大写字母指代不同类型。产生对象时,T和E的类型可以相同也可以不同。

package generic_test;

public class MyPointNew<T,E> {
    private T x;
    private E y;

    public MyPointNew(T x, E y) {
        this.x = x;
        this.y = y;
    }

    public MyPointNew() {

    }

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public E getY() {
        return y;
    }

    public void setY(E y) {
        this.y = y;
    }

    public static void main(String[] args) {
        MyPointNew<String,Integer> pointNew = new MyPointNew<>();
        pointNew.setX("123");
        pointNew.setY(10);
        MyPointNew<Integer,Integer> pointNew1 = new MyPointNew<>();
        pointNew1.setX(10);
        pointNew1.setY(10);
    }
}

泛型方法

泛型除了可以用在类声明上,也可以单独来定义方法。一个类是普通类仍然也可以定义泛型方法。

public class MyClass {
    public <T> T test(T t) {
        System.out.println(t);
        return t;
    }
}

 

在泛型类中定义了泛型方法

泛型方法始终以自己的类型参数为准,与泛型类中的T无关。
推荐若泛型类中存在了泛型方法,定义为不同的类型参数,不造成异议。

泛型声明在接口中

一旦一个接口使用泛型声明,子类在实现接口时就有两种选择

a. 继续保留泛型

/**
 * 子类在实现接口时,继续保留泛型,子类也不清楚具体的类型
 */
public class MessageImpl1<T> implements IMessage<T>{
    @Override
    public void print(T t) {
        System.out.println(t);
    }
}

b. 子类明确当前类型

/**
 * 子类实现接口时明确当前类型
 */
public class MessageImpl2 implements IMessage<String> {
    @Override
    public void print(String s) {
        System.out.println(s);
    }
}

通配符 ?

有泛型之后,使用泛型类或者接口对象,方法的参数不好定义。

引入通配符,只能定义在方法中,表示可以接收所有类型的泛型类。

问题:能否在fun方法中调用set类的方法修改值?

此时定义fun方法时,根本都不知道会传入什么类型的msg对象,无法设置值。

只能调用getter类的方法,无法设置具体值。

泛型上限

? extends类:设置泛型上限,可以用在类和方法声明上。

问题:能否调用setter方法设置值?
不能调用setter方法

现在明确知道的是父类,Number设置也只能设置Number的值。由于不知道传入的子类类型,父类的Number类型的值强制赋值给子类,向下转型不一定成功。


泛型上限两点说明

1. 可以定义在方法参数和类中,明确此时泛型的上限为某个具体的类。
2. 设置的父类,不同的子类之间没有直接关系,因此仍然无法设置某个具体值。

泛型下限

<?super类>规定泛型下限

 问题:能否调用setter方法设置值?
可以!

此时明确知道的是子类,不管?是什么类型,都一定是String的父类。设置一个String对象,天然都is a父类。


泛型下限两点说明

a. 由于此时明确的是下限,明确的是子类是谁,由于子类天然是父类,所以可以设置值。
b. super下限通配符只能用在方法内部,不能在类中定义。

类型擦除

泛型只是编译阶段的语法糖。泛型类和普通类进入JVM之后,没有任何区别。

javac ——> *.java编译为*.class之后,泛型就没了。
类型擦除:泛型信息只存在于编译阶段,进入JVM后,会将所有和泛型相关的信息擦除掉。

若没有规定泛型上限,则所有泛型信息都擦除为Object类型。若规定了泛型上限,则擦除为相应的泛型上限类型。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瘦皮猴117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值