u03-数据类型

155 阅读12分钟

1. 基本数据类型

概念:

  • java是一门强调类型的语言,所以对java语言来说,数据类型是一个很重要的概念。
  • 数据类型分为基本类型和引用类型,基本数据类型分为数值型和非数值型。
  • 基本数据类型一共有8个分别为:
    • 字节型 byte,占1字节,范围是 -2^7到2^7 - 1
    • 短整形 short,占2字节,范围是 -2^15到2^15 - 1
    • 整数型 int,占4字节,范围是 -2^31到2^31 - 1
    • 长整型 long,占8字节,范围是 -2^63到2^63 - 1
    • 单精度浮点型 float,占4字节,范围是 -3.4E38到3.4E38
    • 双精度浮点型 double,占8字节,范围是 -1.7E308到1.7E308
    • 字符型 char,占2字节,是一个Unicode字符。
    • 布尔型 boolean,占1字节,仅存false或true这两个值。
  • 引用数据类型包括类,接口,数组等。

1字节位图

image.png 源码: /javase-start/

  • src: c.y.type.BasicDataTypeTest.build()
/**
 * @author yap
 */
public class BasicDataTypeTest {

    @Test
    public void build() {
        byte byteNum = 100;
        System.out.println(byteNum);

        short shortNum = 100;
        System.out.println(shortNum);

        int intNum = 100;
        System.out.println(intNum);

        long longNum = 100L;
        System.out.println(longNum);

        float floatNum = 100.0F;
        System.out.println(floatNum);

        double doubleNum = 100.0;
        System.out.println(doubleNum);

        boolean flag = false;
        System.out.println(flag);

        char character = 'A';
        System.out.println(character);
    }

1.1 整数型

概念:

  • 整数常量均默认为int类型。
  • 因为整数常量都是确定的值,所以在编译期间,javac会对整数常量先进行范围检查:
    • 如果通过检查,则直接赋值,不涉及任何转换工作。
    • 如果不通过则尝试进行类型转换,转换失败则会报错。
  • 如果常量本身超出默认类型范围,javac不通过:
    • System.out.println(10000000000);:整数常量"10000000000",被认为是int类型,数值超过int最大值,javac报错。
    • System.out.println(10000000000L);:整数常量"10000000000L",直接被认为是long类型,javac通过。

源码: /javase-start/

1.2 浮点型

概念:

  • 为什么叫浮点数: 3.14是个浮点数,但因为java支持科学计数法,所以3.14可以写成 0.314e10.0314e231.4e-1 等等,你会发现在不改变数值的最终结果的前提下,小数点的位置,其实是可以随意浮动的,这就是为何叫做浮点数的原因。
  • 浮点数天生不精确:
    • 1到10之间只可能有10个整数,却有无穷多个浮点数,用有限的容器来存储无限的可能,就一定会有精度受损的时候,如 syout(2.00 - 1.10);
    • 因为浮点数天生不精确,所以绝对不能用浮点数进行比较判断是否相等,如果非要进行特别精确的计算,可以使用 java.math.BigDecimal 中的 subtract() 来完成。
  • 浮点数默认类型: 浮点数常量默认为double类型,因为浮点数天生不精准,所以javac无法对浮点数常量进行范围检查,也就表示所有的double类型数据,都无法直接被float接收。

源码: /javase-start/

1.3 字符型

概念: char类型可以直接赋值整数,也同样会在赋值前接受范围检查,因为char遵循的是Unicode标准,所以它支持的范围是0-65535,在这个范围内,便可成功赋值,此时char类型变量中存储的是字符集中对应位置上的字符。

源码: /javase-start/

1.4 布尔型

概念: 布尔类型虽然占据1字节,但是它占据这8bit(1byte)中的一位,为了区别数字类型,它存在于最高位,即符号位,其余7bit都是0,处于无用状态。

源码: /javase-start/

1.5 基本类型转换

概念:

  • 转换原则:
    • 八大基本数据类型之间(除了boolean之外)都是可以互相转换的。
    • 由小到大自动转,由大到小需强转。
      • int(4) -> long(8):无论你int是多少,long都装得下。
      • long(8) -> int(4):int有可能装不下long的值。
  • 转换格式:
    • 比如,将A转为B,则可以写: B b = (B)A;
  • 浮点数与整数的转换:
    • 浮点数转成整数的时候,会自动将小数点后面的小数全部割弃。
    • 整数转成浮点数的时候,会在整数后面补充 .0 作为小数部分
  • char以数字的方式参与转换:
    • char类型可以和其他类型之间进行转换,是以字符集中,对应字符的编号数字来参与转换的。
  • 不同类型的计算原则:
    • 不同类型之间计算,结果一定返回最大类型。
    • char类型可以进行数学计算,此时char类型先将字符转换为字符集中对应字符的位置编号,然后再进行计算。

源码: /javase-start/

2. 引用数据类型

概念: 除了基本数据类型之外,都是引用数据类型,包括类、数组、接口等等。

2.1 String类型

概念: java.lang.String 是我们学到的第一个引用数据类型,表示字符串。

  • 格式:变量类型 变量名 = new 构造函数();
  • 理解:变量类型 变量名 = new 变量类型();
    • eg: A a = new A();
    • eg: String str = new String("Hello");
  • 特权:String可以使用基本数据类型的声明方式。

java.lang 这个包下的所有的类可以直接使用。

源码: /javase-start/

  • src: c.y.type.StringTest.build()
/**
 * @author yap
 */
public class StringTest {

    @Test
    public void build() {
        String strA = "abc";
        String strB = new String("abc");
        System.out.println(strA);
        System.out.println(strB);
    }
}

2.2 API与API文档

概念: API(Application Programming Interface)就是应用程序编程接口,而API文档是记录这些接口的工具,习惯性翻阅API文档来学习一个类,是一个程序神的必备技能。

  • API官方概念
  • JDK8官网API文档
  • API学习方法:
    • 名称:代码需要使用名字去调用它,但是不需要记忆。
    • 修饰:是否为静态?非静态方法使用实例调用,静态方法使用类名调用。
    • 用途:这个必须记住,否则就跟没学一样。
    • 参数:要求传入什么类型的变量,就传入什么类型的变量,不需要记忆。
    • 返回值:有返回值就用一个对应类型的变量去接收它的返回值,不需要记忆。

3. 正则表达式概念

概念:

  • 正则表达式,又称为规则表达式,Regular Expression,在代码中一般简写为 regex 或者 RE,是对字符串操作的一种逻辑公式,是用事先定义好的一些特定字符、及这些特定字符的组合,组成的一个"规则字符串模板",用来表达对字符串的一种过滤逻辑。
  • 正则表达式很灵活、有逻辑,可以迅速地用极简单的方式达到字符串的复杂控制。
  • 一个正则表达式由三部分组成:
    • 普通字符:数字和字母,如 abc123 等。
    • 特殊字符:具有功能的特殊字符,也称为元字符,如 \d\s 等。
    • 限定字符:限定个数的字符,如 {10}{5, 9}+? 等。
  • 字符串对空格是敏感的,不要在字符串中随意使用空格。

^$ 表示一个正则的开始和结束的精准定位,^表示开头,$表示结尾,需要视情况选择是否使用。

3.1 普通字符

概念:

  • [abc]:匹配 "abc" 中的任意一个字符
    • 成功案例:"a""b"
    • 失败案例:"d""ab"
  • [^abc]:匹配除 "abc" 以外的任意一个字符
    • 成功案例:"d""e"
    • 失败案例:"a""de"
  • [a-z]:匹配a到z之间的任意一个字符
    • 成功案例:"a""b"
    • 失败案例:"A""38"
  • [^a-z]:匹配除了a到z之间的任意一个字符
    • 成功案例:"A""38"
    • 失败案例:"a""b"

"|" 表示或者,[]不能省略。

3.2 特殊字符

概念:

  • .:匹配除 \n\r 之外的任意一个字符
    • 成功案例:"a""%"
    • 失败案例:"\n""\r"
  • \d:匹配一个数字字符,等价于 [0-9]
    • 成功案例:"0""9"
    • 失败案例:"e""10"
  • \D:匹配一个非数字字符,等价于 [^0-9]
    • 成功案例:"e""10"
    • 失败案例:"0""9"
  • \n:匹配一个换行符
    • 成功案例:"\n"
    • 失败案例:"\\n""a"
  • \t:匹配一个制表符
    • 成功案例:"\t"
    • 失败案例:"\\t""a"
  • \w:匹配Unicode字母、数字和下划线
    • 成功案例:"_""a"
    • 失败案例:"\t""&"
  • \W:匹配除了Unicode字母、数字和下划线之外的字符
    • 成功案例:"\t""&"
    • 失败案例:"_""a"

3.3 限定字符

概念:

  • {n}:恰好匹配n次
  • {n,}:至少匹配n次
  • {n,m}:匹配n到m次,n<=m,注意逗号和两个数之间不能有空格
  • *:匹配前面的子表达式任意次,等价于 {0,}
  • +:匹配前面的子表达式一次或多次,等价于 {1,}
  • ?:匹配前面的子表达式零次或一次,等价于 {0,1}

n和m均为非负整数。

3.4 正则三大用途

概念:

  • 数据验证:用RE和某个字符串进行匹配,通过的返回布尔类型结果来分析该字符串是否满足规则。
    • 配合字符串的 matches() 完成验证。
    • "18210210122".matches("^1\\d{10}$")
  • 替换文本:用RE来识别文档中的特定文本,完全删除它,或者用其他文本替换它。
    • 配合字符串的 replaceAll() 完成替换。
    • "My Name Is 9527".replaceAll("[a-z]", "-");
    • 这里不要使用 "^" 或者 "$",的格式,否则将只会替换字符串的第一个或最后一个满足要求的元素。
  • 提取子串:用RE从某个字符串中提取一部分内容,这部分内容称为子字符串。
    • 配合字符串的 split() 完成字符串切割。
    • "Test A. Test B. Test C.".split("\\.\\s*");
    • 这里不要使用 "^" 或者 "$",的格式,否则将只会替换字符串的第一个或最后一个满足要求的元素。

4. JVM内存分布

概念:

  • java程序在运行的时候,绝大部分的数据都在一块叫做运行时数据区(Runtime Data Area)的内存区域中活动。
  • 运行时数据区,被划分成三块小的区域,分别被称为:
    • 栈内存:stack,相当于小区物业,空间小,功能少,访问方便。
    • 堆内存:heap,相当于小区住宅区,空间大,功能多,访问麻烦。
    • 方法区:Method Area,相当于小区广场,公共区域,所有人都可以访问。

4.1 内存地址

概念:

  • 基本数据类型全都分布在栈内存中。
  • 引用数据类型的值分布在堆内存中,内存地址分布在栈内存中。
  • 内存地址也叫引用或者句柄,就像住宅区居民楼的门牌号,全部登记在物业,这样当我去找赵四的时候,只需要去栈中查找赵四家的门牌号就可以直接找到他家,并不需要去一个门一个门去敲问。

源码: 分析如下三行代码在内存中的分布:

int a = 100;
double b = 10.5;
String str = new String("java");

4.2 相等比较之==

概念:

  • 基本类型在使用 == 进行比较的时候,直接按照数学规则比较变量值。
  • 基本类型在使用 == 进行比较的时候,会将参与比较的双方都转换成同一类型,然后再进行比较,转换的目标类型,以参与比较的双方类型中,字节数大的一方为准。
  • 引用类型在使用 == 进行比较的时候,比较的是内存地址。

源码: /javase-start/

  • src: c.j.type.StringTest.compareReference()
    @Test
    public void compareReference() {
        String str01 = new String("yap");
        String str02 = new String("yap");

        // str01 和 str02 有着不同的内存地址,返回F
        System.out.println(str01 == str02);
    }

4.3 相等比较之equals()

  • 基本类型没有方法,所以无法使用 equals() 方法进行比较。
  • equals() 方法来自于 Object 类(继承),原本的作用也是对内存地址进行比较,但是 String 类对其进行了重新改造(Override),使其变成了比较类型和值的一个方法。
  • String类中的 equals() 方法先比较内存地址,如果内存地址不同,则比较字符串的内容。

5. String工具类

概念: 一个String对象的长度和内容都是不可变的,虽然使用"+"可以达到改变内容的目的,但实质会产生一个或多个新的字符串,如果这种改变很频繁,那就会特别浪费内存,如果你的操作中需要频繁进行字符串的拼接,不建议使用 "+"。

时间戳:距离1970年1月1日 0点0时0分 一个毫秒数。

源码: /javase-start/

  • src: c.y.type.StringToolTest.plusSignStitchingTimeConsuming()
/**
 * @author yap
 */
public class StringToolTest {

    @Test
    public void plusSignStitchingTimeConsuming() {
        // 获取时间戳:距离1970年1月1日 0点0时0分 一个毫秒数。
        long startTime = System.currentTimeMillis();

        String str = "";
        for (int i = 0; i < 100000; i++) {
            str = str + i;
        }

        long endTime = System.currentTimeMillis();
        System.out.println("总耗时:" + (endTime - startTime) + "毫秒");
    }

5.1 String拓展类

概念:

  • StringBuilder是jdk1.5版本提出来的一个类,它是一个可变长的字符串类,可以预分配缓冲区,我们可以通过它来进行频繁的字符串拼接操作。
  • 创建方式:StringBuilder stringBuilder = new StringBuilder("a");
  • 拼接方式:append()
  • 虽然在JDK1.8版本中,"+"的底层代码,也是在调用StringBuilder这个类的append()方法,但是多次调用"+"的时候,会创建多次StringBuilder,一样会导致效率低下,这里的优化仍未做到最好,所以在效率上,使用StringBuilder仍然要比使用"+",更优秀。

StringBuffer是JDK1.0时代就存在的老员工了,它可以算是StringBuilder的亲生哥哥,它和StringBuilder的方法都是类似的,唯一的区别是,StringBuffer是线程安全的,而StringBuilder是线程不安全的。

源码: /javase-start/

  • src: c.y.type.StringToolTest.stringBuilderStitchingTimeConsuming()
    @Test
    public void stringBuilderStitchingTimeConsuming() {
        long startTime = System.currentTimeMillis();
        StringBuilder stringBuilder = new StringBuilder("");
        for (int i = 0; i < 100000; i++) {
            stringBuilder.append(i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("总耗时:" + (endTime - startTime) + "毫秒");
    }

5.2 String拓展类常用API

概念: StringBuilder和StringBuffer的api方法类似,这里以StringBuilder为例:

  • replace(n1, n2, "a"):将n1到n2之间的元素,全部替换成字符串"a"。
  • insert(n1, "a"):在n1之后,插入字符串"a"。
  • delete(n1, n2):将n1到n2之间的元素,全部删除。
  • deleteCharAt(n1):删除n1位置上的元素。
  • substring(n1, n2):截取出n1到n2之间的所有元素并返回。
  • reverse():水平翻转字符串。
  • toString():以字符串形式展示。
以上范围均包括n1,但是不包括n2。

6. 包装类

概念: 八个基本数据类型都有相对应的引用数据类型,叫做包装类。

  • Byte/Short/Integer/Long/Float/Double 的父类是 Number
  • Number 的兄弟类是 BooleanCharacter

源码: /javase-start/

  • src: c.y.type.PackingTypeTest.build()
/**
 * @author yap
 */
public class PackingTypeTest {

    @Test
    public void build() {
        System.out.println(new Byte((byte) 100));
        System.out.println(new Short((short) 200));
        System.out.println(new Integer(10000));
        System.out.println(new Long(10000000L));
        System.out.println(new Double(12.5));
        System.out.println(new Float(12.5F));
        System.out.println(new Character('a'));

        // System.out.println(new Boolean(true));
        // Boolean类型包装了更建议的一种声明方式
        System.out.println(Boolean.TRUE);
    }

6.1 装箱

概念: 基本类型转成对应包装类的过程叫做装箱。

  • 在jdk1.5版本之前,需要我们手动装箱。
    • Integer.valueOf(num)
  • 在jdk1.5版本之后,JVM自动拆装箱,代码上直接使用等号赋值即可。

源码: /javase-start/

  • src: c.y.type.PackingTypeTest.manualBoxing()
    @Test
    public void manualBoxing() {
        int num  = 10;
        Integer result = Integer.valueOf(num);
        System.out.println(result);
    }

6.2 拆箱

概念: 包装类转成对应基本类型的过程叫做拆箱。

  • 在jdk1.5版本之前,需要我们手动拆箱。
    • num.intValue()
  • 在jdk1.5版本之后,JVM自动拆装箱,代码上直接使用等号赋值即可。

源码: /javase-start/

  • src: c.y.type.PackingTypeTest.manualUnBoxing()

    @Test
    public void manualUnBoxing() {
        Integer num = new Integer(10);
        int result = num.intValue();
        System.out.println(result);
    }u03-数据类型.md