Java语言基础(SE)-第四节 常用数据类型

枚举(Enum)

  1. 自定义类实现枚举效果
    1. 其实跟枚举的本质差不多
     public class Season1 {
         private Season1() {}
         public static final Season1 SPRING = new Season1();
         public static final Season1 SUMMER = new Season1();
         public static final Season1 FALL = new Season1();
         public static final Season1 WINTER = new Season1();
     }
        
     //main函数使用
     Season1 s1 = Season1.SUMMER;
     if (s1 == Season1.SPRING ) {
     }else if (s1 == Season1.SUMMER) {
     }else if (s1 == Season1.FALL) {
     }else if (s1 == Season1.WINTER) {
     }
    
  2. 枚举类型(Enum Type)
    1. 如果一个变量的取值只可能是固定的几个值,可以考虑使用枚举类型
      1. 枚举由一组预定义的常量构成
     public enum Season {
         SPRING,SUMMER,FALL,WINTER
     }
     Season s = Season.WINTER;
     //WINTER
     System.out.println(s.name());
     //3
     System.out.println(s.ordinal());
     switch (s) {
         case SPRING:
             System.out.println("春天");
             break;
         case SUMMER:
             System.out.println("夏天");
             break;
         case FALL:
             System.out.println("秋天");
             break;
         case WINTER:
             System.out.println("冬天");
             break;
            	
         default:
             break;
     }
    
  3. 枚举的使用注意
    1. 枚举的本质是类,所有枚举类型最终都隐式继承自 java.lang.Enum
    2. 枚举定义完常量后,可以再定义成员变量、方法等内容(这时最后一个常量要以分号结束)
    3. 枚举的构造方法权限必须是 无修饰符 或者 private
      1. Java 会主动调用构造方法初始化每一个常量(就是枚举值),你不能主动调用构造方法
     public enum Season {
         //一旦定义成员,需要加分号结束
         SPRING,SUMMER,FALL,WINTER;
         private int age;
         private void test() {
         }
         //构造方法
         //会调用4次,分别初始化4个枚举常量
         private Season() {
         }
         //public不能修饰
         //public Season() {
         //}
     }
    
  4. 自定义了构造方法的枚举

     public enum Season {
         SPRING(5,15),
         SUMMER(25,35),
         FALL(15,25),
         WINNER(-5,5);
         private int min;
         private int max;
         private Season(int min,int max) {
             this.min = min;
             this.max = max;
         }
         public int getMax() {
             return max;
         }
         public int getMin() {
             return min;
         }
     }
    

数字(Number)

  1. 基本类型的缺陷
    1. 对比引用类型,基本类型存在的一些缺陷
      1. 无法表示不存在的值(null 值)
      2. 不能利用面向对象的方式去操作基本类型(比如直接用基本类型调用方法)
      3. 当方法参数是引用类型时,基本类型无法传递
    2. 解决方案:可以自己将基本类型包装成引用类型
     public class IntObject {
         public int value;
         public IntObject(int value) {
             this.value = value;
         }
     }
     IntObject[] data = {
         new IntObject(-100),
         new IntObject(100),
         null,
         new IntObject(0)
     };
     for (IntObject intObject : data) {
         if(intObject == null) {
             System.out.println("没有值");
         }else {
             System.out.println(intObject.value);
         }
     }
    

包装类(Wrapper Class)

  1. 其实 Java 中已经内置了基本类型的包装类(都在 java.lang 包中)

     基本类型        包装类
     byte            Byte
     char            Character
     short           Short
     int             Integer
     long            Long
     float           Float
     double          Double
     boolean         Boolean
    
  2. 数字类型的包装类(Byte\Short\Integer\Long\Float\Double)最终都继承自 java.lang.Number
  3. 自动装箱、拆箱(Autoboxing and Unboxing)
    1. 自动装箱:Java 编译器会自动将基本类型转换为包装类(调用 valueOf 方法)
    2. 自动拆箱:Java 编译器会自动将包装类转换为基本类型(调用 xxxValue 方法)

       //自动装箱
       Integer i1 = 10;
       //Integer i1 = Integer.valueOf(10);
       //void add(Integer num) {
       //};
       //add(20);
       //add(Integer.valueOf(20));
       //正确
       Object num = 10;
              	
       //自动解包装
       Integer  i2 = 10;
       int i3 = i2;
       //int i3 = i2.intValue();
       System.out.println(i1 == 10);
       //System.out.println(i1.intValue() == 10);
       Integer[] arr = {11,22,33};
       int result = 0;
       for (Integer i : arr) {
           //	i.intValue()%2 == 0
           if(i%2 == 0) {
               //result += i.intValue();
               result +=i;
           }
       }
      
  4. 包装类的判等
    1. 包装类的判等,不要使用 ==、!= 运算符,应该使用 equals 方法

       Integer i1 = 88;
       Integer i2 = 88;
       Integer i3 = 888;
       Integer i4 = 888;
       //不推荐,引用类型用==表示地址比较
       System.out.println(i1 == i2); //true
       System.out.println(i3 == i4); //false
       //推荐
       System.out.println(i1.equals(i2));
       System.out.println(i3.equals(i4));
              
       Integer i5 = 88; //创建对象,存入缓存
       //从缓存中取
       Integer i6 = Integer.valueOf(88);
       //直接创建新的对象
       Integer i7 = new Integer(88) ;
       System.out.println(i5 == i6); //true
       System.out.println(i6 == i7); //false
      
    2. IntegerCache 类中缓存了 [-128, 127] 范围的 Integer 对象

      1. Integer.valueOf 方法会优先去 IntegerCache 缓存中获取 Integer 对象
      2. 88是从缓存从去,所以2个地址一样;888超过127,所以每次都是重新创建,地址不一样
  5. 使用注意
    1. 【基本类型数组】与【包装类数组】之间是不能自动装箱、拆箱的

       public static void test1(Integer[] nums) {}
       public static void test2(int[] nums) {}
              
       int[] nums1 = {11,22};
       test1(nums1); //error
       Integer[] nums2 = nums1;//error
       Integer[] nums3 = {11,22};
       test2(nums3);//error
       int[] nums4 = nums3;//error
      

Math、Random、UUID

Math

  1. java.lang.Math 类提供了常见的数学计算功能

     Math.abs(-100);
     Math.max(100, 200);
     Math.min(100, 200);
     Math.floor(3.9);//向下取整3.0
     Math.ceil(3.1); //向上取整4.0
     Math.round(3.5);//四舍五入,4
     Math.pow(4, 2); //4的2次方,16.0
     Math.sqrt(16); //16的平方根 4.0
     Math.random();//生成[0.0,1.0)范围的随机数
    

Random

  1. java.util.Random 可以更方便地生成各种随机数
//生成各种随机数
Random r = new Random();
r.nextBoolean();
r.nextInt();
r.nextLong();
r.nextFloat();
//生成[0-99]的随机数
int num1 = (int) (Math.random()*100);
//生成[10-99]的随机数
int num2 = new Random().nextInt(90)+10;
	
//输出4位的大写字母验证码
Random rr = new Random();
for(int i = 0; i<4;i++) {
    char c = (char)(rr.nextInt(26)+'A');
    System.out.print(c);
}

UUID

  1. UUID(Universally Unique Identifier),通用唯一标识符
  2. UUID 的目的是让分布式系统中的所有元素都能有唯一的标识符,而不需要通过中央控制端来做标识符的指定
  3. 可以利用 java.util.UUID 类的 randomUUID 方法生成一个 128 bit(32 位 16 进制数)的随机 UUID

     //d1847689-7870-4661-ba50-4d01e25d5f13
     System.out.println(UUID.randomUUID());
    

数字格式化

  1. 可以使用 System.out.printf 或者 System.out.format 输出格式化的字符串
  2. 可以使用 String.format 创建格式化的字符串

     转换符       作用
     d           十进制整数
     f           浮点数
     n           换行,跟 \n 效果一样
        
        
     标记          作用
     08          8个字符的宽度,前面用 0 补齐
     +           显示符号(正数+,负数 )
     ,           显示分组字符(本地化)
     -           左对齐
     .3          保留 3 位小数
     10.3        10 个字符的宽度,保留 3 位小数
    
  3. 举例

     long n = 461012;
     System.out.format("%d%n",n); //461012
     System.out.format("%08d%n",n); //00461012
     System.out.format("%+8d%n",n); // +461012
     System.out.format("%,8d%n",n); // 461,012
     System.out.format("%+,8d%n%n",n); //+461,012
        	
     double pi = Math.PI;
     System.out.format("%f%n",pi); //3.141593
     System.out.format("%.3f%n",pi); //3.142
     System.out.format("%8.3f%n",pi); //   3.142
     System.out.format("%08.3f%n",pi);//0003.142
     System.out.format("%-8.3f%n",pi);//3.142 
     String str = String.format("The PI is %.2f", Math.PI);
     //The PI is 3.14
     System.out.println(str);
    

DecimalFormat

  1. 使用 java.text.DecimalFormat 可以更好地控制前 0、后 0、前缀、后缀、分组分隔符、十进制分隔符等
static void customFormat (String pattern,double value) {
    DecimalFormat fmt = new DecimalFormat(pattern);
    System.out.println(fmt.format(value));
}

//123,456.789
customFormat("###,###.###", 123456.789);
//123456.79
customFormat("###.##", 123456.789);
//000123.780
customFormat("000000.000", 123.78);
//$12,345.67
customFormat("$###,###.###", 12345.67);

字符串转数字

  1. 使用包装类的 valueOf、parseXX 方法
//12
Integer i1 = Integer.valueOf("12");
//12
int i2 = Integer.parseInt("12");
//255
int i3 = Integer.parseInt("FF",16);
//12.34
Float f1 = Float.valueOf("12.34");
//12.34
Float f2 = Float.parseFloat("12.34");

数字转字符串

  1. 使用字符串的 valueOf 方法、包装类的 toString 方法
//12.34
String str1 = String.valueOf(12.34);
//255
String str2 = Integer.toString(255);
//ff
String str3 = Integer.toString(255,16);
//12.34
String str4 = Float.toString(12.34f);

高精度计算

  1. float、double 存储的只是小数的近似值,并非精确值。因此不适合用来进行高精度计算

     double d1 = 0.7;
     double d2 = 0.7;
     //0.48999999999999994
     System.out.println(d1*d2);
    
  2. 建议使用 java.math.BigDecimal 来进行高精度计算
  3. 建议使用字符串初始化 BigDecimal,因为 float、double 存储的是近似值,不是精确值
BigDecimal v1 = new BigDecimal("0.7");
BigDecimal v2 = new BigDecimal("0.7");
System.out.println(v1.add(v2));//1.4
System.out.println(v1.subtract(v2));//减0.0
System.out.println(v1.multiply(v2));//乘0.49
System.out.println(v1.divide(v2));//除以1
System.out.println(v1.setScale(3)); //保留是三位小数0.700

字符串(String)

  1. Java 中用 java.lang.String 类代表字符串
    1. 底层使用 char[] 存储字符数据,从 Java 9 开始,底层使用 byte[] 存储字符数据
    2. 所有字符串字面量都是 String 类的实例
    3. String 对象一旦创建完毕,它的字符内容是不可以修改的
  2. 字符串常量池(String Constant Pool)
    1. Java 中有个字符串常量池(String Constant Pool,简称 SCP)
      1. 从 Java 7 开始属于堆空间的一部分(以前放在方法区)
    2. 当遇到字符串字面量时,会去查看 SCP
      1. 如果 SCP 中存在与字面量内容一样的字符串对象 A 时,就返回 A
      2. 否则,创建一个新的字符串对象 D,并加入到 SCP 中,返回 D
  3. 字符串的初始化

     String s1 = "ab";
     String s2 = new String("ab");
     String s3 = new String(s1);
     String s4 = new String(s2);
     char[] cs = {'a','b'};
     String s5 = new String(cs);
     String s6 = new String(s5);
    

    图1

    1. 上图可以通过打断点来验证

intern 方法

  1. A.intern 方法的作用
    1. 如果 SCP 中存在与 A 内容一样的字符串对象 C 时,就返回 C
    2. 否则,将 A 加入到 SCP 中,返回 A
int a = 1,b=2,c=3;
String s1 = String.format("%d%d%d", a,b,c); //SCP无
String s2 = String.format("%d%d%d", a,b,c);//SCP无
//将s1指向的字符串,加入到SCP中
String s3 = s1.intern();
//s2的指向仍然没有变化,仍然是当初的
String s4 = s2.intern();
String s5 = "123";
	
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//true
System.out.println(s1 == s4);//true
System.out.println(s1 == s5);//true

字符串的常用方法

//去除左右的空格: 123  456
" 123  456  ".trim();
//转为大写字母:ABC
"abc".toUpperCase();
//转为小写字母:abc
"ABC".toLowerCase();
//是否包含某个字符串:true
"123456".contains("34");
//是否以某个字符串开头:true
"123456".startsWith("123");
//是否以某个字符串结尾:true
"123456".endsWith("456");
//将字符串分割为数组:[1,2,3,4]
"1_2_3_4".split("_");
//比较字符串的大小:<0
"abc".compareTo("adc");
//忽略大小写比较字符串大小:<0
"abc".compareToIgnoreCase("ADC");
	
String s1 = "abc";
String s2 = new String("abc");
//查看字符串的内容是否相等
s1.equals(s2);
//忽略大小写查看字符串的内容是否相等:true
"abc".equalsIgnoreCase(s2);
//字符串截取,3456
System.out.println("123456".substring(2,6));
//查找索引:3
System.out.println("123456".indexOf("4"));
//替换666456
System.out.println("123456".replace("123","666"));

StringBuilder

  1. 在进行大量字符串的改动操作时(比如拼接、替换)
    1. 使用 String 会非常消耗内存、降低程序性能
    2. 使用 StringBuilder 可以节省内存、提高程序性能
  2. StringBuilder 的常用方法有:append、insert、delete、replace、reverse等

     String str1 = "";
     str1 += "123";
     str1 += "456";
        	
     StringBuilder str2 = new StringBuilder();
     str2.append("123").append("456");
    
  3. 注意
    1. StringBuilder 并不是 String 的子类 或者 父类
    2. StringBuilder、String 都实现了 CharSequence 接口
  4. StringBuilder 的 append 原理
    1. 本质就是数据结构与算法中的动态数组,根据数据存储的量动态的分配内存
    2. StringBuilder 的默认容量是 16,扩容后的新容量是原来容量的 2 倍 + 2
      1. 16 扩容为 34
      2. 34 扩容为 70
      3. 70 扩容为 142
      4. ..

日期(Date)

  1. Date
    1. java.util.Date 是开发中经常用到的日期处理类(注意:不是 java.sql.Date)
      1. 包含了年、月、日、时、分、秒等信息
       //date1/date2都代表当前时间
       Date date1 = new Date();
       Date date2 = new Date(System.currentTimeMillis());
       //Tue Mar 16 22:58:28 CST 2021
       System.out.println(date1);
       //Tue Mar 16 22:58:28 CST 2021
       System.out.println(date2);
      
    2. System.currentTimeMillis 返回的是从 1970-01-01 00:00:00 GMT 开始到现在经历的毫秒数
      1. 1970-01-01 00:00:00 GMT、1970-01-01 08:00:00 CST代表的是同一个时间
        1. GMT(Greenwich Mean Time):格林尼治时间
        2. CST(China Standard Time UT+8:00):中国标准时间
  2. Date 常用方法

     Date d1 = new Date();
     Date d2 = new Date();
     //设置毫秒数
     d1.setTime(1000);
     d2.setTime(2000);
     //获取毫秒数
     d1.getTime(); //1000
     d2.getTime(); //2000
     //比较时间
     d2.after(d1);//true
     d1.before(d2);//true
     d1.compareTo(d2);//-1
    
  3. SimpleDateFormat
    1. java.text.SimpleDateFormat 常用来进行日期的格式化处理

       SimpleDateFormat fmt = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
       //利用日期对象生成字符串
       String str = fmt.format(new Date());
       //2021年03月16日 23:12:01
       System.out.println(str);
              	
       //解析字符串为日期对象
       Date date = fmt.parse(str);
       //Tue Mar 16 23:12:53 CST 2021
       System.out.println(date);
      

Calendar

  1. java.util.Calendar 也是开发中经常用到的日期处理类
    1. 功能比 Date 更加丰富,Date 中很多过期的方法都迁移到了 Calendar 中
     //表示当前的时间
     Calendar c = Calendar.getInstance();
     c.get(Calendar.YEAR);//年
     //月(取值范围是[0,11],0是1月,11是12月)
     c.get(Calendar.MONTH);
     //一月中的第几天(取值范围[1,31])
     c.get(Calendar.DAY_OF_MONTH);
     //一周中的第几天(取值范围[1,7],1是星期天,。。。7是星期六)
     c.get(Calendar.DAY_OF_WEEK);
     //一年中的第几天(取值范围[1,366])
     c.get(Calendar.DAY_OF_YEAR);
     c.get(Calendar.HOUR);//小时
     c.get(Calendar.MINUTE);//分
     c.get(Calendar.SECOND);//秒
     c.get(Calendar.MILLISECOND);//毫秒
     System.out.println(c);
    
  2. Calendar 的常用方法

     Calendar c = Calendar.getInstance();
     //2019年7月6日
     c.set(2019, 06,06);
     ////2019年7月11日
     c.add(Calendar.DAY_OF_MONTH, 5);
     //2019年9月11日
     c.add(Calendar.MONTH, 2);
     //设置Date对象
     c.setTime(new Date());
     //获得Date对象
     c.getTime();
        	
     //设置毫秒数
     c.setTimeInMillis(System.currentTimeMillis());
     //获取毫秒数
     c.getTimeInMillis();
    
  3. 打印格式化(很少用)
    1. 转换符

       转换符       作用      
       tB          month(本地化)
       td, te      day(若只有 1 位数字,td 会在前面补 0)
       ty, tY      ty = 2-digit year, tY = 4-digit year
       tl          hour(12 小时制)
       tM          minute(若只有 1 位数字,在前面补 0)
       tp          am/pm(本地化)
       tm          month(若只有 1 位数字,在前面补 0)
       tD          %tm%td%ty
      
    2. 代码举例

       Calendar c = Calendar.getInstance();
       Date date = new Date();
       //三月 16,2021
       System.out.format("%tB %te,%tY%n",date,date,date);
       //11:18 下午
       System.out.format("%tl:%tM %tp%n",c,c,c);
       //03/16/21
       System.out.format("%tD%n",c);
      
Table of Contents