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 包中)
| 基本类型 | 包装类 |
|---|---|
| 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、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的效果一样 |
| 标记 | 作用 |
|---|---|
| 08 | 8个字符的宽度,前面用 0 补齐 |
| + | 显示符号(正数+,负数 -) |
| , | 显示分组字符(本地化) |
| - | 左对齐 |
| .3 | 保留 3 位小数 |
| 10.3 | 10 个字符的宽度,保留 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的模式字母
| 字母 | 元素 | 示例 |
|---|---|---|
| G | Era 标志符 | AD |
| y | 年 | 1996; 96 |
| M | 年中的月份 | July; Jul; 07 |
| w | 年中的周数 | 27 |
| W | 月份中的周数 | 2 |
| D | 年中的天数 | 189 |
| d | 月份中的天数 | 10 |
| F | 月份中的星期 | 2 |
| E | 星期中的天数 | Tuesday; Tue |
| a | Am/pm 标记 | PM |
| H | 一天中的小时数(0-23) | 0 |
| k | 一天中的小时数(1-24) | 24 |
| K | am/pm 中的小时数(0-11) | 0 |
| h | am/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
}