Java从零开始 第11讲 泛型和常用类库

结束思维训练

在之前十讲中,我们学习了简单的逻辑,进行面向对象编程的训练,其中实用的东西看起来比较少,这是因为那些东西并不是针对使用的,主要目的是为了锻炼你思考的方式,所做的习题也都是为了帮助你养成编程思维
现在我们将要开始学习Java的常用类库,学习实用的编程

泛型

泛型,即“参数类型化”,在之前讲过声明一个变量必须给定它的类型,而使用泛型,则可以暂时不声明它的类型,而是在调用时再传入具体的类型

泛型类

通过泛型类,能够生成一个未定的数据类型

class ClassName<T>{
    private T data;
}

让我们分析一个实例:泛型Student类

class Student<A>{
    private String name;
    private A data;
    
}

此时对着类中空行处按下alt+insert选择Getter and Setter,并选择全部
在这里插入图片描述
此时就会生成我们定义的两个属性的get和set方法

class Student<A>{
    private String name;
    private A data;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public A getData() {
        return data;
    }

    public void setData(A data) {
        this.data = data;
    }
}

注意在data的方法中,data的参数类型都为自定的A

在生成这个类的对象时,通过如下方式定义,A的类型会变为在<>中的类型
(注意此处不能使用基本类型,必须使用它们的包装类)

        Student<String> student1 = new Student<>();
        Student<Integer> student2 = new Student<>();
        Student<Float> student3 = new Student<>();

泛型接口

泛型接口通过如下方式定义

interface InterfaceName<T>{
    T getData();
}

实现该接口时,可以直接指定参数类型

    class Interface1 implements InterfaceName<String> {
            private String data;
            @Override
            public String getData() {
                  return data;
            }
    }

也可以不指定

    class Interface1<T> implements InterfaceName<T> {
            private T data;
            @Override
            public T getData() {
                return data;
            }
     }    

泛型方法

通过如下方式定义一个泛型方法:

权限修饰符 <T> 返回值类型 方法名(参数列表){}
public static <T> void method(T t){}

泛型方法能传入任何类型的参数

泛型限定符和通配符

在使用泛型时,我们可以限定泛型的使用范围,即除声明的类之外无法使用,如
< T extends className>
< T extends interface1 & interface2>

泛型也可以用?作为通配符,代替具体的类型
< ? extends 父类名>
< ? super 子类名>
< ?>
这样可以限定泛型类型的上下届

泛型详解

泛型的主要作用:

  • 提高代码的复用率
  • 类型安全(使用时指定,不需要强制转化)
    特别提示:编译之后程序会采取一种名为去泛型化的措施,泛型无法存在于运行阶段,即在泛型被使用后会更改为指定类型,这也是和Object类型的主要区别)

常用类

String类

String其实也是一个类,java中所有的字符串其实都是String类的对象/实例,String通过数组的方式实现了不同字符的拼接,且java中所有用双引号""包含的内容都是字符串

String很特殊的一点是,如果两个字符串完全相等,则他们会采用完全相同的地址(与一般类不同),下面我将详细解释

让我们先了解一下关于堆的知识,在之前我们讲过堆中主要用于存储对象,堆内存在逻辑上主要分为三块,新生代(Young Generation),老年代(Old Generation),和永久代(Permanent Gerneration,也可以被成为原空间,Metaspace);新生代中存储着刚创建的对象和经过小于15次GC回收仍然存在的对象,而大于15次GC仍然存在的对象则会存在于老年代中,在新生代中GC会比老年代中要快的多。而永久代中则存放JDK自带的类和接口,不会被GC扫描,也不会被回收,内存只要JVM运行就被占用。

字符串常量池(存储单个字符的地方)会被存于方法区中,可以被所有线程共同实用,而方法区实际上就是堆内存的永久代区域,在物理上就是堆的一部分,为了节省这一部分的内存,相同的字符串就会使用相同的地址

字符串的值其实是不变的,它们的值在创建后无法更改,而我们进行的字符串操作其实都是产生了新的字符串

所以每次拼接字符都会产生一个新的字符串,所以如果多次拼接,就会产生许多垃圾,并且此垃圾在永久代中无法被回收,为了大量拼接字符串我们可以先创建StringBuilder或StringBuffer的对象,使用其中的append方法进行拼接,再用 对象名.toString() 将该对象转化为字符串来减小内存消耗(Builder和Buffer在不涉及线程时完全一样;而他们又经常迭代更新,这里我们就不详细解释了)

java.util.Objects

Object类我们之前介绍过,在java.lang中,是所有类的基类,并且重写过其中的toString和equals方法。
而Objects类在java.util包中,所以需要

import java.util.Objects;
特别注意:除了在java.lang包中的类都需要额外import)

Objects类中有许多基础的方法,比如equals方法
在通常比较字符串时,我们使用equals方法

        String s1 = "123";
        String s2 = "234";
        System.out.println(s1.equals(s2));

在一般的使用中没有问题,但是如果s1是null,那么就会报错
所以我们使用Objects类中的equals方法来比较字符串

        String s1 = "123";
        String s2 = "234";
        System.out.println(Objects.equals(s1,s2));

其中加了非空的检测,可以避免空指针异常

java.lang.Math

Math类中有很多数字运算的方法,比如

Math.abs(num1);// 求绝对值
Math.min(num1, num2);// 返回两数中较小的值
Math.max(num1, num2);// 返回较大的
Math.round(num1);// 四舍五入
Math.floor(num1);// 返回小于等于参数的最大整数
Math.ceil(num1);// 返回大于等于参数的最小整数
Math.random();// 返回double类型随机数,范围为(0, 1)

特别注意:100.5的四舍五入返回101,而-100.5的四舍五入返回-100)

java.util.Arrays

Arrays类中有许多用于操作数组的方法,包括我们之间实现过的二分查找,Arrays类中有很多不同的查找,排序,和表示方法

System.out.println(Arrays.toString(arr1));
// 打印整个数组,用逗号隔开
Arrays.sort(arr1);// 排序该数组
Arrays.binarySearch(arr1, target);// 返回查找下标
Arrays.copyOf(arr1, 15);// 创建一个新数组,拥有原先的元素且长度变为15

java.math.BigDecimal

运算以下代码

        double a = 0.1 + 0.1;
        double b = 0.1 + 0.2;
        System.out.println(a);
        System.out.println(b);

你会发现a的值为0.2,但是b的值却并不是0.3,而是0.30000000000000004,在这里我们就不具体解释原因,我们只需要知道在进行小数运算时很可能出现这种误差

(可以通过如下网址详细了解:0.1 + 0.2 为什么不等于 0.3

此时为了获取更准确的结果,我们就需要用到BigDecimal类中的方法

        BigDecimal bd1 = new BigDecimal("0.1");
        BigDecimal bd2 = new BigDecimal("0.2");
        // BigDecimal bd3 = new BigDecimal("asd"); 将会报错

        BigDecimal a = bd1.add(bd2);
        BigDecimal b = bd1.substract(bd2);
        BigDecimal c = bd1.multiply(bd2);
        BigDecimal d = bd1.divide(bd2);

注意其中的divide方法如果出现循环结果会抛出异常,需要传入一个RouningMode对象,这里我们不详细解释

java.util.Date

Date类主要用于表示时间,精度为毫秒

Date类在JDK1.1之前可以将时间解释为年月日,以及格式化和解析日期字符串。
但是现在年月日和时间字段转化的方法已经放在Calendar类中,格式化和解析方法在DateFormat类中

Date类中现在还没过时的方法主要有:

构造方法Date()和Date(long data)分别用于获取当前的时间,和传入长整型毫秒数并计算该时间对应的时刻

        Date date = new Date();
        System.out.println(date);
        System.out.println(date.toString());

Date类已经重写了toString方法,所以输出结果是一样的,都会输出该对象被创建的时间;而如果构造时传入一个时间,就会指定该对象代表的时间

getTime(),用于返回当前的时间,单位为毫秒(用于1970年1月1日GMT0点0分到现在的毫秒数表示)

java.text.DateFormat

DateFormat主要用于转化Date类的时间为年月日时分秒格式,DateFormat是抽象的,无法直接使用,通常通过其子类SimpleDateFormat使用


        Date date = new Date();
        System.out.println(date);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        String a = simpleDateFormat.format(date);
        System.out.println(a);



运行结果:
Sun Dec 27 13:57:44 CST 2020
2020年12月27日 13:57:44

Process finished with exit code 0

其中的时间为电脑当前显示的时间,并不是联网获取的,传入的参数为你希望显示的格式,yyyy表示四位数的年,MM,dd,HH,mm,ss分别为两位数的月日时分秒,毫秒则用大写的S表示

你也可以通过parse方法传入一个你定义格式的时间,比如我们之间定义的格式
yyyy年MM月dd日 HH:mm:ss

所以用
simpleDateFormat.parse(“2020年12月27日 13:57:44”)
就可以得到传入时间对应的毫秒数

java.util.Calendar

Calendar类也是抽象的类,但是它提供了解释时间的方法,使用Calendar类可以直接调用其中的静态方法

Calendar calendar = Calendar.getInstance();
// 获取现在的时刻
calendar.set(Calendar.YEAR, 2011);
// 设置其中的年为2011
calendar.add(Calendar.YEAR, 1);
// 使其中的年加一
int year = calendar.get(Calendar.YEAR);// 获取年
int day = calendar.get(Calendar.DAY_OF_MONTH);// 获取今天是本月的第几天
calendar.getActualMaximum(Calendar.DAY_OF_MONTH);// 获取此月总共有多少天

其中有很多的get方法,分别对应了不同的变量,使用十分方便;且在add方法中,如果超出值域会自动进位(比如11月+2得到1月,且年份+1,但是0月表示的是1月,所以最大只有12;而星期则是星期天为1,星期六为7)

同样也能用
Date time = calendar.getTime();
获取Date对象

java.lang.System

System中包含许多常用的静态方法

最常用的莫过于System.out.println,这一个陪同我们从Hello World到现在的方法

System.exit也是一个实用的方法,可以直接结束当前进程,注意我们之前所有正常运行完的进程最后都会跟一句
Process finished with exit code 0
而如果用了System.exit(-1);
最后的0就会变为-1

System.currentTimeMillis();也可以得到当前的时间

arraycopy方法也和之前的Arrays方法一样,能够对原数组进行扩容

转载请注明出处

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值