三、常用数据类型

166 阅读11分钟

java基础学习的文章目录

枚举

1、模拟实现

public class Season {
	private Season() {}
	
	public static final Season SPRING = new Season();
	public static final Season SUMMER = new Season();
	public static final Season FALL = new Season();
	public static final Season WINTER = new Season();
}

调用

public class Main {
	public static void main(String[] args) {
		Season s = Season.SUMMER;
		if (s == s.SPRING) {
			System.out.println("春天");
		} else if(s == s.SUMMER) {
			System.out.println("夏天");
		} else if(s == s.FALL) {
			System.out.println("秋天");
		} else if(s == s.WINTER) {
			System.out.println("冬天");
		}
		// 输出:夏天
	}
}

枚举类型

  • 如果一个变量的取值只可能是固定的几个值,可以考虑使用枚举类型
    • 枚举由一组预定义的常量构成
public enum Season {
	SPRING, SUMMER, FALL, WINTER
}

使用

public class Main {
	public static void main(String[] args) {
		Season s = Season.SUMMER;
		System.out.println(s.name()); //SUMMER
		
		System.out.println(s.ordinal()); //1
		
		// 输出夏天
		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(5,15), SUMMER(25,35), FALL(15,25), WINTER(-5,5);
	private int min;
	private int max;
	
	private Season(int min, int max) {
		this.min = min;
		this.max = max;
	}
	
	public int  getMin() {
		return min;
	}
	
	public int getMax() {
		return max;
	}
}

使用

public class Main {
	public static void main(String[] args) {
		Season s = Season.SUMMER;
		System.out.println(s.name()); //SUMMER
		System.out.println(s.ordinal()); //1
		System.out.println(s.getMin()); //25
		System.out.println(s.getMax()); //35
	}
}

2、包装类使用

基本类型的缺陷

  • 对比引用类型,基本类型存在的一些缺陷
    • 无法表示不存在的值(null 值)
    • 不能利用面向对象的方式去操作基本类型(比如直接用基本类型调用方法)
    • 当方法参数是引用类型时,基本类型无法传递
  • 解决方案:可以自己将基本类型包装成引用类型
public class IntObject {
	public int value;
	
	public IntObject(int value) {
		super();
		this.value = value;
	}

	public static void main(String[] args) {
		IntObject[] data = new IntObject[] {
				new IntObject(-100),
				new IntObject(100),
				null,
				new IntObject(0)
		};
		//null 可以表示不存在的情况,和0区别开来
		for (IntObject intObject : data) {
			if (null == intObject) {
				System.out.println("没有值");
			} else {
				System.out.println(intObject.value);
			}
		}
	}
}

包装类(Wrapper Class)

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

基本类型包装类
byteByte
charCharacter
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
  • 数字类型的包装类(Byte\Short\Integer\Long\Float\Double)最终都继承自 java.lang.Number

自动装箱、拆箱(Autoboxing、Unboxing)

  • 自动装箱: Java 编译器会自动将基本类型转换为包装类(调用 valueOf 方法)
  • 自动拆箱: Java 编译器会自动将包装类转换为基本类型(调用 xxxValue 方法)

使用示例:

	public static void main(String[] args) {
		Integer i1 = 10;
		// 相当于:Integer i1 = Integer.valueOf(10);
		
		add(20);
		//相当于:add(Integer.valueOf(20));
		
		Integer i2 = 10;
		int i3 = i2;
		//相当于:int i3 = i1.intValue();
		
		
		System.out.println(i2 == 10);
		// 相当于:System.out.println(i2.intValue() == 10);
		
		Integer[] array = {11,12,13};
		int result = 0;
		for (Integer integer : array) {
			//相当于: integer.intValue()%2 == 0;
			if (integer%2 == 0) {
				result += integer;
				//相当于: result += integer.intValue();
			}
		}
	}
	
	static void add(Integer num) {}

3、包装类细节

包装类的判等

  • 包装类的判等,不要使用 ==、 != 运算符,应该使用 equals 方法
  • IntegerCache 类中缓存了 [-128, 127] 范围的 Integer 对象
    • Integer.valueOf 方法会优先去 IntegerCache 缓存中获取 Integer 对象
	public static void main(String[] args) {
		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)); // true
		System.out.println(i3.equals(i4)); //true
		
		//演示不同创建方法的缓存效果
		Integer i5 = 88;
		Integer i6 = Integer.valueOf(88);
		Integer i7 = new Integer(88);
		System.out.println(i5 == i6); //true
		System.out.println(i5 == i7); //false
	}

使用注意

  • 【基本类型数组】 与【包装类数组】 之间是不能自动装箱、拆箱的
    public static void main(String[] args) {
		int[] nums1 = {11,22};
		test1(nums1);  //error
		Integer[] nums2 = nums1; // error
		
		Integer[] nums3 = {11, 22};
		test2(nums3); // error
		int[] nums4 = nums3;// error
	}

4、Math

  • java.lang.Math 类提供了常见的数学计算功能
	public static void main(String[] args) {
		System.out.println(Math.abs(-100));//绝对值:100
		System.out.println(Math.max(100, 200));//最大值:200
		System.out.println(Math.min(100, 200));//最小值:100
		System.out.println(Math.floor(3.9));//向下取整:3.0
		System.out.println(Math.ceil(3.1));//向上取整:4.0
		System.out.println(Math.round(3.5));//四舍五入:4
		System.out.println(Math.pow(4, 2)); // 4的2次方:16.0
		System.out.println(Math.sqrt(16));//16的平方根:4.0
		System.out.println(Math.exp(2));//e的二次方:7.38905609893065
		System.out.println(Math.log(8)); //以E为低,8为真数的对数:2.0794415416798357
		
		double degree = 90;
		double radian = Math.toRadians(degree);
		System.out.println(Math.sin(radian)); //1.0
	}

Random

	public static void randomTest() {
		//生成【0.0,1.0)的随机数
		System.out.println(Math.random());
		
		Random r = new Random();
		System.out.println(r.nextBoolean());;
		System.out.println(r.nextInt());
		System.out.println(r.nextLong());
		System.out.println(r.nextFloat());
		
		//生成【0,99】范围的整数
		int num1 = (int)(Math.random()*100);
		System.out.println(num1);
		
		int num2 = new Random().nextInt(100);
		System.out.println(num2);
		
		//生成【10,99】范围的随机数
		int num3 = (int)(Math.random()*90) + 10;
		int num4 = new Random().nextInt(90) + 10;
		
		System.out.println(num3);
		System.out.println(num4);
	}

示例:

	public static void randomCaptha() {
		//输出4位的大写字母验证码
		for (int i = 0; i < 4; i++) {
			char c = (char)(new Random().nextInt(26) + 'A');
			System.out.print(c);
		}
	}

UUID

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

示例代码:

	public static void uuid() {
		//输出: cf7c961c-0f97-48f8-a68d-7b3d81a0da83
		System.out.println(UUID.randomUUID());
	}

5、数字格式

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

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

示例代码

	public static void format() {
		long n = 353456;
		System.out.format("%d%n", n);
		System.out.format("%08d%n", n);
		System.out.format("%+8d%n", n);
		System.out.format("%,8d%n", n);
		System.out.format("%+,8d%n", n);
	}

输出

353456
00353456
 +353456
 353,456
+353,456

double格式化示例:

	public static void formatDouble() {
		double pi = Math.PI;
		System.out.format("%f%n", pi);
		System.out.format("%.3f%n", pi);
		System.out.format("%8.3f%n", pi);
		System.out.format("%08.3f%n", pi);
		System.out.format("%-8.3f%n", pi);
	}

输出

3.141593
3.142
   3.142
0003.142
3.142   
	// String format示例代码
	public static void formatString() {
		String str = String.format("The PI is %.2f", Math.PI);
		System.out.println(str);
	}

DecimalFormat

  • 使用 java.text.DecimalFormat 可以更好地控制前 0、后 0、前缀、后缀、分组分隔符、十进制分隔符等

示例:

	public static void formatDecimal() {
		customFormat("###,###.###", 123456.789);
		
		customFormat("###.##", 123456.789);
		
		customFormat("000000.000", 123.78);
		
		customFormat("$###,###.###", 12345.78);
	}
	
	public static void customFormat(String pattern, double value) {
		DecimalFormat f = new DecimalFormat(pattern);
		System.out.println(f.format(value));
	}

输出:

123,456.79
123456.79
000123.780
$12,345.78

字符串转数字

  • 使用包装类的 valueOf、 parseXX 方法 使用示例
	public static void stringToNumber() {
		//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 方法 使用示例:
	public static void numberToString() {
		String str1 = String.valueOf(12.34); //12.34
		String str2 = Integer.toString(255); //255
		String str3 = Integer.toString(255, 16); //ff
		String str4 = Float.toString(12.34f); //12.34
	}

高精度计算

  • float、 double 存储的只是小数的近似值,并非精确值。因此不适合用来进行高精度计算
	//float 不能进行高精度计算
	public static void floatMutiply() {
		double d1 = 0.7;
		double d2 = 0.7;
		//0.48999999999999994
		System.out.println(d1*d2);
	}
  • 建议使用 java.math.BigDecimal 来进行高精度计算

BigDecimal

使用示例

	public static void bigDecimal() {
		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));//保留3位小数 0.700
	}

6、String01

  • Java 中用 java.lang.String 类代表字符串
    • 底层使用 char[] 存储字符数据,从 Java 9 开始,底层使用 byte[] 存储字符数据
    • 所有字符串字面量(比如 )都是 String 类的实例
    • String 对象一旦创建完毕,它的字符内容是不可以修改的
	public static void stringConst() {
		String s = "555";
		s+="555";
		s = "666";
		testString(s);
		System.out.println(s);
	}
	public static void testString(String s) {
		s += "555";
	}

字符串常量池(String Constant Pool)

  • Java 中有个字符串常量池(String Constant Pool,简称 SCP)
    • 从 Java 7 开始属于堆空间的一部分(以前放在方法区)
  • 当遇到字符串字面量时,会去查看 SCP
    • 如果 SCP 中存在与字面量内容一样的字符串对象 A 时,就返回 A
    • 否则,创建一个新的字符串对象 D,并加入到 SCP 中,返回 D
	public static void scpTest() {
		String s1 = "lj";
		String s2 = "lj";
		//表明s1 和 s2 指向相同的字符串对象
		System.out.println(s1 == s2); // true
	}

构造函数:

    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

string内部的数组分析:

	public static void stringInit() {
		// s1、s2、s3、s4是四个不同的指针,存储string对象的地址,这四个string对象中的value指向一致
		String s1 = "lj";
		String s2 = new String("lj");
		String s3 = new String(s1);
		String s4 = new String(s2);
		
		char[] cs = {'l', 'j'};
		// 数组创建字符串,会拷贝一份数组
		String s5 = new String(cs);
		String s6 = new String(s5);
		//s5、s6内部的数组指向同一份,和cs不同
	}

思考

  • 为什么使用字符串初始化内部的字符数组结构不变,而使用数组初始化,内部会拷贝一份数组?
  • 答:字符串设计为常量,不可变,使用相同的内部数组不会导致彼此受影响,而数组是能改变的,修改数组会导致字符串改变,所以需要单独拷贝一份。

字符串初始化

7、String——intern

◼ A.intern 方法的作用 如果 SCP 中存在与 A 内容一样的字符串对象 C 时,就返回 C 否则,将 A 加入到 SCP 中,返回 A

	public static void stringIntern() {
		int a = 1, b = 2, c = 3;
		String s1 = String.format("%d%d%d", a, b, c);
		String s2 = String.format("%d%d%d", a, b, c);
		String s3 = s1.intern();
		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
	}

8、String方法

String 常用方法

	public static void stringMethod() {
		String s1 = "   123   456".trim(); // 去除左右的空格
		String s2 = "abc".toUpperCase(); //ABC
		String s3 = "ABC".toLowerCase();//abc
		boolean b4 = "123456".contains("34");//true
		boolean b5 = "123456".startsWith("123");//true
		boolean b6 = "123456".endsWith("456");//true
		String[] s7 = "1_2_3_4".split("_"); //[1, 2, 3, 4]
		int i8 = "abc".compareTo("abc");//0
		int i9 = "abc".compareToIgnoreCase("ABC");//0
		
		String s10 = "abc";
		String s11 = new String("abc");
		boolean b10 = s10.equals(s11);//true
		boolean b11 = "abc".equalsIgnoreCase("aBC");//true
	}

9、StringBuilder

  • 在进行大量字符串的改动操作时(比如拼接、替换)
    • 使用 String 会非常消耗内存、降低程序性能
    • 使用 StringBuilder 可以节省内存、提高程序性能
  • StringBuilder 的常用方法有: append、 insert、 delete、 replace、 reverse等
  • 注意
    • StringBuilder 并不是 String 的子类 或者 父类
    • StringBuilder、 String 都实现了 CharSequence 接口

StringBuilder 的 append 原理

  • StringBuilder 的默认容量是16,扩容后的新容量是原来容量的 2 倍 + 2
    • 16 扩容为 34
    • 34 扩容为 70
    • 70 扩容为 142
    • ......

10、Date、Calendar

  • java.util.Date 是开发中经常用到的日期处理类(注意:不是 java.sql.Date)
    • 包含了年、月、日、时、分、秒等信息
	public static void date() {
		// date1 和date2 都代表当前时间
		Date date1 =  new Date();
		Date date2 = new Date(System.currentTimeMillis());
		//Sat Feb 11 16:58:43 CST 2023
		System.out.println(date1);
		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):中国标准时间

Date常用方法

	public static void dateMethod() {
		Date date1 = new Date();
		Date date2 = new Date();
		
		//设置毫秒数
		date1.setTime(1000);
		date2.setTime(2000);
		
		//获取毫秒数
		System.out.println(date1.getTime());//1000
		System.out.println(date2.getTime());//2000
		
		//比较时间
		System.out.println(date2.after(date1));//true
		System.out.println(date1.before(date2));//true
		System.out.println(date1.compareTo(date2));//-1
	}

SimpleDateFormat

  • java.text.SimpleDateFormat 常用来进行日期的格式化处理
	public static void simpleDateFormat() throws ParseException {
		SimpleDateFormat fmt = new SimpleDateFormat("yyy年MM月dd日 HH:mm:ss");
		String str = fmt.format(new Date());
		System.out.println(str); //2023年02月11日 20:12:11
		
		Date date = fmt.parse(str);
		System.out.println(date); //Sat Feb 11 20:12:11 CST 2023
	}

SimpleDateFormat的模式字母

字母元素示例
GEra 标志符AD
y1996; 96
M年中的月份July; Jul; 07
w年中的周数27
W月份中的周数2
D年中的天数189
d月份中的天数10
F月份中的星期2
E星期中的天数Tuesday; Tue
aAm/pm 标记PM
H一天中的小时数(0-23)0
k一天中的小时数(1-24)24
Kam/pm 中的小时数(0-11)0
ham/pm 中的小时数(1-12)12
m小时中的分钟数30
s分钟中的秒数55
SS毫秒数978
z时区Pacific Standard Time;PST; GMT-08:00
Z时区-0800

Calendar

  • java.util.Calendar 也是开发中经常用到的日期处理类
    • 功能比 Date 更加丰富, Date 中很多过期的方法都迁移到了 Calendar 中
	public static void calendar() {
		Calendar c = Calendar.getInstance();
		System.out.println(c.get(Calendar.YEAR)); //2023
		System.out.println(c.get(Calendar.MONTH)); //1
		System.out.println(c.get(Calendar.DAY_OF_MONTH));//11
		System.out.println(c.get(Calendar.DAY_OF_WEEK));//7
		System.out.println(c.get(Calendar.DAY_OF_YEAR));//42
		System.out.println(c.get(Calendar.HOUR));//8
		System.out.println(c.get(Calendar.MINUTE));//29
		System.out.println(c.get(Calendar.SECOND));//39
		System.out.println(c.get(Calendar.MILLISECOND));//387
	}