常用类
一、内部类
在类的内部定义的类。
在一个类中,根据定义的方式不同,变量可以分为:
- 实例变量(成员变量、属性)
- 静态变量
- 局部变量
- 匿名变量
public class Outter {
String name; // 实例变量
static int n; // 静态变量
public void test() {
int m; // 局部变量
System.out.println(new int[] {1,2,3,4,5}.length); // 匿名变量
}
}
在类的内部定义的类,与变量的操作方式相似,所以也分为:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
1.1 成员内部类
在使用时参考成员变量的使用,需要先创建外部类的对象再使用。
public class Outter {
public String name = "outter==name"; // 实例变量
class Inner{
public String name = "inner==name";
public int age;
// public static int m = 5; // 不能定义静态变量
public void test() {
// 调用自己的属性
System.out.println("inner" + this.name);
// 调用外部的属性
System.out.println("inner" + Outter.this.name);
}
// 不能定义静态方法
// public static void test1() {
//
// }
}
}
public class TestMain {
public static void main(String[] args) {
// 先创建对象后使用
// Outter o = new Outter();
// o.name = "hello";
//
// Inner inner = o.new Inner();
// inner.age = 20;
// inner.test();
// 必须先创建外部类的对象才能创建内部类的对象
Inner i1 = new Outter().new Inner();
i1.test();
}
}
1.2 静态内部类【重点】
在类的内部使用static定义的类,操作与普通的类基本一样,一般用于将一个类封装在类的内部。例如在Arrays类内部定义的ArrayList类,就只是在Arrays类的内部使用,外部无法访问。
public class Outter1 {
public static String name;
public void a() {
Inner1 n = new Inner1();
}
public static class Inner1{
public int age;
public static String sex;
public void test() {
}
public static void test1() {
}
}
}
public class TestMain1 {
public static void main(String[] args) {
Inner1 inner1 = new Inner1();
inner1.age = 20;
inner1.test();
Inner1.test1();
}
}
1.3 局部内部类
在方法内部定义的类,仅在方法内部使用。
public class Outter2 {
public void test() {
class Inner2{
private int age;
public void setAge(int age) {
this.age = age;
}
public void hello() {
System.out.println(age);
}
}
// 创建对象并使用
Inner2 i2 = new Inner2();
i2.setAge(20);
i2.hello();
}
}
1.4 匿名内部类[重点]
在类的内部使用的没有名字的类。
一般需要接口或者抽象类来实现。
直接创建接口的对象并将该接口中的所有方法都实现。此时实际上系统会动态生成一个类,并实现该接口,该类的名字由于是动态生成的,是不可见的,称为匿名内部类。
一般用在某些需要实现接口或者继承父类的类,该类在项目中只使用一次。
public interface MyInterface {
void hello();
}
public class Outter3 {
public void test() {
MyInterface i = new MyInterface() {
@Override
public void hello() {
System.out.println(this.getClass().getName());
}
};
i.hello();
// new MyInterface() {
// @Override
// public void hello() {
// System.out.println("hello");
// }
// }.hello();
// MyInterface m = ()->System.out.println("hello");
// m.hello();
}
}
二、Object类
Object类是所有类的顶层父类,所有的类直接或者间接继承了Object类,所有的类都具备有Object类中的方法。
如果使用Object类作为参数或者返回值,能适用于所有的类型。
2.1 getClass()
返回对象的实际类型,可以用来比较对象类型是否一致。
public class Person {
public void feed(Animal a) {
System.out.println(a.getClass());
a.eat();
// if(a instanceof Dog) {
// //
// }
if(a.getClass() == Dog.class) { // 比较类型是否相同
System.out.println(a.getClass().getName());
}
}
}
2.2 hashCode()
返回该对象的十进制的哈希码值。
使用哈希算法,即根据对象的地址或者字符串,或数字计算出来的值。
哈希码并不是唯一的。
注意:应该保证相同的对象具有相同的哈希码,尽量保证不同的对象具有不同的哈希码。
字符串的哈希码通过字符串组成的字符的ascii计算,数字使用数字计算,自定义对象使用地址计算。
2.3 equals()
用来比较对象是否相同,会调用此方法。
equals和==的区别:
- ==只会比较对象的地址。
- 通过equals比较,如果没有重写Object类的equals,那么也是比较地址,此时与==相同,但是可以重写equals方法,自己定义对象的比较方式。
public class Student {
public String id;
public String name;
public int age;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)// 先处理快速比较
return true;
if (obj == null)// 先排除错误情况
return false;
if (getClass() != obj.getClass())// 如果不是一个类型,不予比较
return false;
Student other = (Student) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
2.4 toString()
用来描述当前对象的,如果不重写,父类会直接输出类名和地址,没有意义,应该在子类中重写。
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
2.5 finalize()
用来标记垃圾对象,进入回收队列,由JVM自动调用。
三、包装类
是指基本数据类型所对应的引用数据类型。
注意:基本数据类型作为属性或数组元素,有相应的默认值,例如int的默认值为0,但是如果使用包装类,则默认值为null。
8个基本数据类型对应的包装类名,除了int对应Integer,char对应Character以外,其他的都是该单词首字母大写,例如:float对应Float,double对应Double。
3.1 基本用法
public class Test1 {
public static void main(String[] args) {
int n = 5;
// 将基本数据类型转换成包装类
Integer in1 = new Integer(n); // 直接创建
Integer in2 = Integer.valueOf(n); // 使用自带的方法转化
Integer in3 = n; // 自动装箱,jdk1.5以后的功能
Integer in4 = 10;
// 将包装类转换成基本数据类型
int n1 = in1.intValue();
// 自动拆箱,jdk1.5以后的功能
int n2 = in2;
// 将字符串转换成数字,如果转换失败会报错NumberFormatException
String str = "23";
int n3 = Integer.parseInt(str);
String str1 = "23.5";
double u1 = Double.parseDouble(str1);
// 将一个十进制的数字转换成2进制,并以字符串的形式呈现
String s1 = Integer.toBinaryString(23);
System.out.println(s1);
// 将一个十进制的数字转换成8进制,并以字符串的形式呈现
String s2 = Integer.toOctalString(2334535);
System.out.println(s2);
// 将一个十进制的数字转换成16进制,并以字符串的形式呈现
String s3 = Integer.toHexString(2353454);
System.out.println(s3);
double d = 5.0;
Double d1 = new Double(d);
Double d2 = Double.valueOf(d);
Double d3 = d;
double u = d1.doubleValue();
Integer i1 = new Integer(5);
Integer i2 = new Integer(5);
System.out.println(i1 == i2);
Integer i3 = Integer.valueOf(5);
Integer i4 = Integer.valueOf(5);
System.out.println(i3 == i4);
Integer i5 = Integer.valueOf(128);
Integer i6 = Integer.valueOf(128);
System.out.println(i5 == i6);
}
}
3.2 自动装箱拆箱
在JDK1.5之后,加入了自动装箱拆箱功能,即包装类和其对应的基本数据类型之间可以相互赋值。
public class Test1 {
public static void main(String[] args) {
int n = 5;
// 将基本数据类型转换成包装类
Integer in1 = new Integer(n); // 直接创建
// 自动装箱
Integer in4 = 10;
// 自动拆箱,jdk1.5以后的功能
int n2 = in2;
}
}
3.3 整数常量池
整数常量池是系统在一开始就在内存中创建了一个Integer类型的数组,一般情况下保存了256个常用Integer整数,范围为-128~127之间,当使用该范围的整数时,不用重新创建对象,直接返回数组中的相应对象,以减少对象的创建和内存的消耗。
注意:使用Integer.valueOf()或者直接Integer in = 3;才会去取常量池中的值,如果使用new Integer()还是会创建对象。
// 来源于Integer类的源代码
public static Integer valueOf(int i) {
// 如果在该范围内,会直接返回数组中已创建的对象,否则会new一个新对象
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
四、字符串
4.1 基本理论
String类是final的,不允许继承
String存储数据是使用char型数组
String类型的字面值是不能改变的,字符串的值一旦创建是不能改变的,只能重新创建一个新的对象
- 值使用char数组存储,该数组使用final修饰,所以不能重新创建一个新数组来赋值
- 该数组也没有提供通过数组下标修改元素的办法,所以,里面的元素也无法改变
- 该类不能被继承,也无法通过重写方法修改方法中的逻辑达到修改元素的办法,何况该数组是私有的,即使有子类也无法访问。
4.2 字符串的常量池
当直接使用String str = "hello";时,是在常量池中创建hello,那么多次使用地址是一样的,可以通过==比较是否相等。但是使用String str = new String("hello"),是在堆中创建的对象,对象中创建过程中,先引用了常量池中的"hello",但是str中存放的堆中的地址,所以不能使用==比较,只能使用equals比较。
注意:String类中intern方法,可以将字符串重新指向常量池的地址。
public class Test2 {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
str3 = str3.intern(); // 得到常量池中的地址
System.out.println(str1==str3); // true
}
4.3 字符串作为参数传递【重点】
字符串的值是不能变的,所以传递到方法中,方法中如果想改变该字符串,一定会创建新的字符串,对外面的字符串没有影响,如果想将改变后的字符串返回,一定要接收返回值。
public class Test2 {
public static void main(String[] args) {
String str4 = "bbb";
test(str4);
System.out.println(str4); // 结果是bbb
}
public static void test(String s) {
s = "aaa"; // 改变了地址的指向,对原地址没有影响
}
}
注意:包装类作为参数传递与字符串相似。
public class Test2 {
public static void main(String[] args) {
Integer in = 5;
test1(in);
System.out.println(in); // 结果是5
}
public static void test1(Integer n) {
n = 3;
}
}
4.4 字符串编译时优化【重点】
字符串连接时,如果都是常量,在编译时会直接优化成一个字符串。
public class Test3 {
public static void main(String[] args) {
final String a = "a";
final String b = "b";
String a1 = "a";
String b1 = "b";
String ab = "ab";
// 会直接优化成"ab"
String s1 = "a" + "b";
// 会优化,因为是常量
String s2 = a + b;
// 不会优化,因为是变量
String s3 = a1 + b1;
// 返回new String("ab")
String s4 = "a".concat("b");
// 直接返回ab
String s5 = "ab".concat("");
// 返回常量池的ab
String s6 = s4.intern();
System.out.println(ab == s5);
}
}
4.5 字符串常用方法
char charAt(int index); 得到字符串中某个位置的字符。
boolean contains(String str); 判断是否包含某个字符串。
int indexOf(String str); 找到字符串中包含某个字符串的起始位置,如果不存在,则返回-1
char [] toCharArray(); 返回字符串中包含的数组的一个拷贝
int length(); 得到字符串的长度
String trim(); 去掉字符串两边的空格
String substring(int beginIndex); 从下标beginIndex(包含下标beginIndex的位置)开始截取,截取后面所有的
String substring(int beginIndex, int endIndex); 从下标beginIndex(包含下标beginIndex的位置)开始截取,截取到下标endIndex(不包含下标endIndex)
String toUpperCase(); 将字符串转换成大写
String toLowerCase(); 将字符串转换成小写
boolean endsWith(String str); 判断是否以某个字符串结尾
boolean startsWith(String str); 判断是否以某个字符串开头
String s.replace("oldString", "newString"); 替换字符串中的内容
String[] str5.split("符号"); 将字符串切割成一个数组
String String.join(",", array); 将数组使用某个字符串拼接成一个字符串
public class Test4 {
public static void main(String[] args) {
String s = "hello, world";
char ch = s.charAt(0); // 得到某个位置的字符
System.out.println(ch);
boolean b = s.contains("world"); // 判断是否包含某个字符串
System.out.println(b);
// 找到字符串中包含某个字符串的起始位置,如果不存在,则返回-1
int index = s.indexOf("word");
System.out.println(index);
// 返回字符串中包含的数组的一个拷贝
char [] chs = s.toCharArray();
System.out.println(Arrays.toString(chs));
// 得到字符串的长度
int len = s.length();
System.out.println(len);
String s1 = " hello, world ";
// 去掉字符串两边的空格
s1 = s1.trim();
System.out.println(s1);
// 从下标3(包含下标3的位置)开始截取,截取后面所有的
String str = s.substring(3);
System.out.println(str);
// 从下标3(包含下标3的位置)开始截取,截取到下标8(不包含下标8)
// 范围:[3, 8)
String str1 = s.substring(3, 8);
System.out.println(str1);
// 将字符串转换成大写
String str2 = s.toUpperCase();
System.out.println(str2);
// 将字符串转换成小写
String str3 = str2.toLowerCase();
System.out.println(str3);
// 判断是否以某个字符串结尾
boolean b1 = s.endsWith("rld");
System.out.println(b1);
// 判断是否以某个字符串开头
boolean b2 = s.startsWith("hel");
System.out.println(b2);
// 替换字符串中的内容
String str4 = s.replace("l", "L");
System.out.println(str4);
// 将字符串切割成一个数组
String str5 = "湖北,湖南,江西,福建";
String[] strings = str5.split(",");
System.out.println(Arrays.toString(strings));
// 将数组拼接成一个字符串
String [] arr = {"aaa", "bbb", "ccc", "ddd", "eee"};
String str6 = String.join(",", arr);
System.out.println(str6);
}
}
五、可变字符串
普通字符串的值不可变,如果经常需要修改字符串的值,那么可以使用可变字符串,减少字符串的创建次数和空间的浪费。特别是需要频繁拼接字符串时,应该使用可变字符串。
StringBuffer:JDK1.0提供,线程安全,效率低
StringBuilder:JDK5.0提供,线程不安全,效率高
public class Test5 {
public static void main(String[] args) {
// 创建时可以指定大小,如果不指定,默认为16
// 当超出了数组大小时,会扩容,每次扩容为原来的2倍+2
StringBuilder sb = new StringBuilder(40); // 默认为长度为16
sb.append("hello1="); // 拼接内容
sb.append("world2="); // 拼接内容
sb.append("hello3="); // 拼接内容
sb.append("world4="); // 拼接内容
sb.append("hello5="); // 拼接内容
sb.append("world6="); // 拼接内容
sb.append("hello7="); // 拼接内容
sb.append("world8="); // 拼接内容
System.out.println(sb);
// 删除delete(start, end)范围:[start, end)
sb.delete(3, 8);
System.out.println(sb);
// 修改replace(start, end, str)
sb.replace(3, 8, "AAA");
System.out.println(sb);
// 插入insert(offset, str)
sb.insert(3, "BBB");
System.out.println(sb);
}
}
注意:在用法和使用方式上,StringBuilder和StringBuffer没区别。
六、BigDecimal
主要有两个作用:
- 解决浮点数的精度问题
- 解决数字计算超出最大范围的问题
public class Test6 {
public static void main(String[] args) {
// 精度问题
double d = 1.0 - 0.9;
System.out.println(d);
double d1 = 237943729 / 67.0;
System.out.println(d1);
// 当创建大数字对象时,一定一定一定要使用字符串
BigDecimal b1 = new BigDecimal("1.0");
BigDecimal b2 = new BigDecimal("0.9");
// 减法
b1 = b1.subtract(b2);
System.out.println(b1);
// 加法
b1 = b1.add(b2);
System.out.println(b1);
// 超出数字范围问题
// 乘法
BigDecimal b3 = new BigDecimal("34759347567863453453453453456345346345645645645645645645678689345798");
BigDecimal b4 = new BigDecimal("4589085308503485903845908349058390485348590348590834905834590348");
b3 = b3.multiply(b4);
System.out.println(b3);
// 除法
// 当两个数字能除尽,则可以不指定小数位长度,否则需要指定小数位长度,不然会报错
BigDecimal b5 = new BigDecimal("5");
BigDecimal b6 = new BigDecimal("3");
// 小数长度位200位,最后结果四舍五入
b5 = b5.divide(b6, 20, BigDecimal.ROUND_HALF_UP);
System.out.println(b5);
}
}
- 创建对象时,一定要传入字符串,避免不精确或超出范围
- 当使用除法时,注意是否需要指定小数位以及四舍五入等参数,避免报错
七、日期时间相关类
7.1 Date日期类
常用的日期类,
public class Test7 {
public static void main(String[] args) {
Date now = new Date(); // 当前时间
System.out.println(now);
// 距离GMT 1970-1-1 0:0:0的毫秒数
System.out.println(now.getTime());
// before和after方法用来比较两个Date对象的早晚
}
}
7.2 Calendar日历类
public class Test8 {
public static void main(String[] args) {
// 不能通过new创建对象,使用下面的方法得到唯一的对象,因为是单例模式
Calendar c = Calendar.getInstance();
// c.set(Calendar.MONTH, 0);
// c.set(Calendar.DAY_OF_MONTH, 7);
System.out.println(c);
// 将Calendar转换成Date
System.out.println(c.getTime());
c.setTime(new Date()); // 将Date转换成Calendar
System.out.println(c.getTimeInMillis()); // 得到毫秒数
// 得到年
int year = c.get(Calendar.YEAR);
System.out.println(year);
// 得到当年的第多少周
int weeks = c.get(Calendar.WEEK_OF_YEAR);
System.out.println(weeks);
// 得到月份,从0开始
int month = c.get(Calendar.MONTH);
System.out.println(month);
// 得到星期,周日为1,周一为2
int week = c.get(Calendar.DAY_OF_WEEK);
System.out.println(week);
// 时间的计算,可以使用负数,表示向前
c.add(Calendar.WEEK_OF_MONTH, -88);
System.out.println(c.getTime());
}
}
八、字符串与时间的转换SimpleDateFormat
public class Test9 {
public static void main(String[] args) throws ParseException {
Date now = new Date();
// 格式化日期
// yyyy年,MM月,dd日,hh12小时制时,a上午下午,HH24小时制时,mm分,ss秒,SSS毫秒
SimpleDateFormat sdf = new SimpleDateFormat("中国yyyy年MM月dd日 ahh时mm分ss秒SSS毫秒");
// 将日期格式化成字符串
String str = sdf.format(now);
System.out.println(str);
// 将字符串转换成日期对象
String str1 = "2021-10-10 14:12:12";
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = sdf1.parse(str1);
System.out.println(d1);
}
}
九、System系统类
public class Test10 {
public static void main(String[] args) {
// 得到距离...毫秒数
System.out.println(System.currentTimeMillis());
// 提示系统可以回收垃圾
// System.gc();
// 停止系统(杀死)
System.exit(0);
// 不会执行
System.out.println("程序结束");
}
}