Java语言基础(SE)-第四节 常用数据类型
枚举(Enum)
- 自定义类实现枚举效果
- 其实跟枚举的本质差不多
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) { }
- 枚举类型(Enum Type)
- 如果一个变量的取值只可能是固定的几个值,可以考虑使用枚举类型
- 枚举由一组预定义的常量构成
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; }
- 如果一个变量的取值只可能是固定的几个值,可以考虑使用枚举类型
- 枚举的使用注意
- 枚举的本质是类,所有枚举类型最终都隐式继承自 java.lang.Enum
- 枚举定义完常量后,可以再定义成员变量、方法等内容(这时最后一个常量要以分号结束)
- 枚举的构造方法权限必须是 无修饰符 或者 private
- Java 会主动调用构造方法初始化每一个常量(就是枚举值),你不能主动调用构造方法
public enum Season { //一旦定义成员,需要加分号结束 SPRING,SUMMER,FALL,WINTER; private int age; private void test() { } //构造方法 //会调用4次,分别初始化4个枚举常量 private Season() { } //public不能修饰 //public Season() { //} }
-
自定义了构造方法的枚举
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)
- 基本类型的缺陷
- 对比引用类型,基本类型存在的一些缺陷
- 无法表示不存在的值(null 值)
- 不能利用面向对象的方式去操作基本类型(比如直接用基本类型调用方法)
- 当方法参数是引用类型时,基本类型无法传递
- 解决方案:可以自己将基本类型包装成引用类型
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)
-
其实 Java 中已经内置了基本类型的包装类(都在 java.lang 包中)
基本类型 包装类 byte Byte char Character short Short int Integer long Long float Float double Double boolean Boolean
- 数字类型的包装类(
Byte\Short\Integer\Long\Float\Double
)最终都继承自java.lang.Number
- 自动装箱、拆箱(Autoboxing and Unboxing)
- 自动装箱:Java 编译器会自动将基本类型转换为包装类(调用 valueOf 方法)
-
自动拆箱: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; } }
- 包装类的判等
-
包装类的判等,不要使用 ==、!= 运算符,应该使用 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
-
IntegerCache 类中缓存了 [-128, 127] 范围的 Integer 对象
- Integer.valueOf 方法会优先去 IntegerCache 缓存中获取 Integer 对象
- 88是从缓存从去,所以2个地址一样;888超过127,所以每次都是重新创建,地址不一样
-
- 使用注意
-
【基本类型数组】与【包装类数组】之间是不能自动装箱、拆箱的
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
-
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
- 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
- UUID(Universally Unique Identifier),通用唯一标识符
- UUID 的目的是让分布式系统中的所有元素都能有唯一的标识符,而不需要通过中央控制端来做标识符的指定
-
可以利用 java.util.UUID 类的 randomUUID 方法生成一个 128 bit(32 位 16 进制数)的随机 UUID
//d1847689-7870-4661-ba50-4d01e25d5f13 System.out.println(UUID.randomUUID());
数字格式化
- 可以使用 System.out.printf 或者 System.out.format 输出格式化的字符串
-
可以使用 String.format 创建格式化的字符串
转换符 作用 d 十进制整数 f 浮点数 n 换行,跟 \n 效果一样 标记 作用 08 8个字符的宽度,前面用 0 补齐 + 显示符号(正数+,负数 ) , 显示分组字符(本地化) - 左对齐 .3 保留 3 位小数 10.3 10 个字符的宽度,保留 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
- 使用 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);
字符串转数字
- 使用包装类的 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");
数字转字符串
- 使用字符串的 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);
高精度计算
-
float、double 存储的只是小数的近似值,并非精确值。因此不适合用来进行高精度计算
double d1 = 0.7; double d2 = 0.7; //0.48999999999999994 System.out.println(d1*d2);
- 建议使用 java.math.BigDecimal 来进行高精度计算
- 建议使用字符串初始化 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)
- Java 中用 java.lang.String 类代表字符串
- 底层使用 char[] 存储字符数据,从 Java 9 开始,底层使用 byte[] 存储字符数据
- 所有字符串字面量都是 String 类的实例
- String 对象一旦创建完毕,它的字符内容是不可以修改的
- 字符串常量池(String Constant Pool)
- Java 中有个字符串常量池(String Constant Pool,简称 SCP)
- 从 Java 7 开始属于堆空间的一部分(以前放在方法区)
- 当遇到字符串字面量时,会去查看 SCP
- 如果 SCP 中存在与字面量内容一样的字符串对象 A 时,就返回 A
- 否则,创建一个新的字符串对象 D,并加入到 SCP 中,返回 D
- Java 中有个字符串常量池(String Constant Pool,简称 SCP)
-
字符串的初始化
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);
- 上图可以通过打断点来验证
intern 方法
- A.intern 方法的作用
- 如果 SCP 中存在与 A 内容一样的字符串对象 C 时,就返回 C
- 否则,将 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
- 在进行大量字符串的改动操作时(比如拼接、替换)
- 使用 String 会非常消耗内存、降低程序性能
- 使用 StringBuilder 可以节省内存、提高程序性能
-
StringBuilder 的常用方法有:append、insert、delete、replace、reverse等
String str1 = ""; str1 += "123"; str1 += "456"; StringBuilder str2 = new StringBuilder(); str2.append("123").append("456");
- 注意
- StringBuilder 并不是 String 的子类 或者 父类
- StringBuilder、String 都实现了 CharSequence 接口
- StringBuilder 的 append 原理
- 本质就是数据结构与算法中的动态数组,根据数据存储的量动态的分配内存
- StringBuilder 的默认容量是 16,扩容后的新容量是原来容量的 2 倍 + 2
- 16 扩容为 34
- 34 扩容为 70
- 70 扩容为 142
- ..
日期(Date)
- Date
- java.util.Date 是开发中经常用到的日期处理类(注意:不是 java.sql.Date)
- 包含了年、月、日、时、分、秒等信息
//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);
- System.currentTimeMillis 返回的是从 1970-01-01 00:00:00 GMT 开始到现在经历的毫秒数
- 1970-01-01 00:00:00 GMT、1970-01-01 08:00:00 CST代表的是同一个时间
- GMT(Greenwich Mean Time):格林尼治时间
- CST(China Standard Time UT+8:00):中国标准时间
- 1970-01-01 00:00:00 GMT、1970-01-01 08:00:00 CST代表的是同一个时间
- java.util.Date 是开发中经常用到的日期处理类(注意:不是 java.sql.Date)
-
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
- SimpleDateFormat
-
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
- java.util.Calendar 也是开发中经常用到的日期处理类
- 功能比 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);
-
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();
- 打印格式化(很少用)
-
转换符
转换符 作用 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
-
代码举例
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);
-