JavaSE常用类总结

大小写切换:CTRL+shift+U
方法的重写
在子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法
只是方法体中的实现不同,以实现不同于父类的功能,扩展增强。
这种方式被称为方法重写(override),又称为方法覆盖。当父类中的方法无法满足子类需求或子类具有特有功能的时候,需要方法重写。
Object类
Java里所有的类都有一个共同的父类Object,不管你愿不愿意都得继承他(默认继承,不用加extends)
此外,还有许多在这些类上+s的比如Objects类,表示是Object的工具类
备胎男
所以,如果你不重写方法(比如equals,toString等等)就会默认调用Object类里面的方法,产生不一样的结果!
比如Obj类里面的equals和String类里面的equals是不一样的!
toString类
用于输出类的值
在jdk帮助文档中给出明确介绍,会返回一个
getClass().getName() + '@' + Integer.toHexString(hashCode())
重写:
alt+ins 选择toString()即可
这里idea会让你选择输出类的属性,比如一个学生类,选择输出id,name,sex,里面的一种或几种
hashcode类
C语言实现 没有源代码
为不同的对象返回不同的整数int 有些时候obj的hashcode就是你在内存中的地址(如果不重写)
如果重写 hashcode禁止相同!!确定对象的唯一性
每次重写hashcode的时候都会绑定一个equals方法
equals类
如何比较两个对象 基本数据类型:值 引用数据类型:内存地址
== 比较地址 指针操作 Obj类里面equals比较的是值 源码用的还是==
case1
当我们遇到这种情况,比较两个对象是否相等?对象有很多元素,比如id,name,date,age...但是我们判断的唯一标准选择id即可。这时需要重写equals类。
上面这个代码建议把hashcode里面的参数和euqals保持一致 夫妻一体同心
case2
当然,如果要比较的对象是组合关系,比如下面这段。必须!!!重写自定义属性类的equals方法。否则就默认调用obj的equals(==)
public static void main(String[] args) {
Computer mac = new Computer(111,"mac", new Cpu(11,"m1"));
Computer win = new Computer(111,"win", new Cpu(11,"i7"));
//内存地址不等
System.out.println(mac==win);
//属性的值
System.out.println(mac.equals(win));
}
Computer类里的元素包含了Cpu类
class Computer{
public int id;
public String name;
public Cpu cpu;
public Computer(int id, String name, Cpu cpu) {
this.id = id;
this.name = name;
this.cpu = cpu;
}
cpu类
class Cpu{
public int id;
public String name;
public Cpu(int id, String name) {
this.id = id;
this.name = name;
}
case3
如果一个类有1000个属性。有8个对象,如何找到两个相等的呢?
答:先比较hashcode,再equals
这俩是夫妻 hashcode具有假唯一性(大部分的元素hashcode都不想等 但是不排除有相等的情况)
盒子原理:把三个苹果放到两个盒子中要求每个盒子至少有一个苹果 那么肯定有一个盒子中装了两个苹果
所以hashcode和euqals的重写是一起的,并且equals选择了哪些元素,hashcode就一定选择哪些(当然,也可以比equals少,equals更严格)
即只要equals了他们的hashcode一定相等!!! 反过来不成立
题外话——类的构造函数
快捷键:alt+ins
此时会自动生成一个有参的构造函数,在主函数里面给对象的属性赋值的时候就会自动帮你完成一些初始化操作。
如果没有再定义一个无参构造函数的话上面new对象的代码会报错。
Teacher lisa = new Teacher(111, "lisa");
Teacher bob = new Teacher(111, "bob");
如果没有定义有参构造,这样写即可
Teacher lisa = new Teacher();
但是根据秦老师说的如果写了有参构造,就必须把无参构造也写去!!!
这样new的时候怎么写都不会报错啦~
clone类
浅克隆
首先借助Object.clone()方法
克隆类要实现Cloneable接口,但他是一个标记接口(空的)
重写Object.clone()方法,提高访问权限,把protected改成public
对引用的克隆对象进行转型
抛出CloneNotSupportedException异常或
class Desk implements Cloneable{
public int id;
public String name;
public Desk(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Desk{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Desk desk = (Desk) o;
return id == desk.id && Objects.equals(name, desk.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
主函数
public static void main(String[] args) throws CloneNotSupportedException{//抛出异常
Desk desk1 = new Desk(1,"blues");
//desk2的引用指向了desk1
//Desk desk2 = desk1;
Desk desk2= (Desk) desk1.clone();//转型
System.out.println(desk2==desk1);//虽然是克隆的 副本 但是地址不一样 ==比较地址
System.out.println(desk2.equals(desk1));//重写后是true
}

深克隆

深克隆方法:
浅克隆的他都要有,本质就是对克隆对象的子类对象也进行一遍克隆,实际上也是一样的操作
对子类也implements cloneable
重写clone函数改成public
再修改一下总对象(小汽车)的clone方法
新建一对象(即克隆后的小汽车)将此对象的属性也都克隆
class car implements Cloneable{
public int id;
public Engine engine;
public Tire tire;
public car(int id, Engine engine, Tire tire) {
this.id = id;
this.engine = engine;
this.tire = tire;
}
public car(){
}
@Override
public Object clone() throws CloneNotSupportedException {
//deepc是被克隆对象
car deepc=(car) super.clone();//super是基类
//engine的深度克隆
deepc.engine= (Engine) this.engine.clone();//克隆完返回的是obj,需要强转 this本类
//tire的深度克隆
deepc.tire= (Tire) this.tire.clone();
return deepc;
}
@Override
public String toString() {
return "car{" +
"id=" + id +
", engine=" + engine +
", tire=" + tire +
'}';
}
}
class Engine implements Cloneable{
public int eid;
public String ename;
public Engine(int eid, String ename) {
this.eid = eid;
this.ename = ename;
}
public Engine() {
}
@Override
public String toString() {
return "Engine{" +
"eid=" + eid +
", ename='" + ename + '\'' +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Tire implements Cloneable{
public int tid;
public String tname;
public Tire(int tid, String tname) {
this.tid = tid;
this.tname = tname;
}
public Tire() {
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
主函数
public static void main(String[] args) throws CloneNotSupportedException{
car car1 = new car(11,new Engine(221,"v8"),new Tire(331,"mql"));
car car2= (car) car1.clone();
System.out.println(car1.toString());
System.out.println(car2.toString());
System.out.println("===========");
car1.engine.ename="ved";
System.out.println(car1.toString());
System.out.println(car2.toString());
}
boxing类
Java不是完全面向对象的语言,因为他有一些基本数据类型。
在设计类的时候为每一个基本数据类型设计了一个对应的类代表,即包装类(wrapper,boxing)
包装类的名字基本和原来类型一致,除了int和char

final修饰的类 太监类 不能被继承 里面的方法也不能重写
装箱:基本数据类型--->包装类
拆箱:包装类--->基本数据类型
编译器大部分情况会帮你主动转换 如下
//创建对象
Integer i1=new Integer(10);//被废弃了
Integer i2=new Integer("20");//被废弃了
Integer i3=3;//自动装箱boxing
//推荐写法
Integer i4=Integer.valueOf(4);
//拆箱
int i5=Integer.valueOf(5);
int i6=Integer.valueOf(6).intValue();
缓存问题
//根据我们的创建方式,如果不new并且传入的数字小于128,存在int常量池中(用的缓存)
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1==i2);//true
Integer i5 = Integer.valueOf(128);
Integer i6 = Integer.valueOf(128);
System.out.println(i5==i6);//true
//只要newl就有地址了,堆内存中创建新的对象,但是new的方法被废弃了,上面的更智能
Integer i3 = new Integer(10);
Integer i4 = new Integer(10);
System.out.println(i3==i4);//false

时间类
date方法
过期了 不建议用
时间处理
计算机并不会存储时间的数据
时间在计算机中就是一个数字,以毫秒为单位
时间/1000/60/60/24
//当前时间
Date date = new Date();
System.out.println(date);
//获取指定时间,笨比方法,直接算
Date d2 = new Date(1000L*60*60*24*365*50);
System.out.println(d2);
//获取时间所对应的毫秒数
System.out.println(date.getTime());
//对象的比较
Date da =new Date();
Date db =new Date();
System.out.println(da==db);
System.out.println(da.equals(db));//重写equals噜
//查看源码 调用了gettime拿到了毫秒数 进行比较
Calendar方法
匿名内部类 常用 建议使用
不能直接new,抽象类!
获取指定日期属性对应的值
Calendar calendar = Calendar.getInstance();
//System.out.println(calendar.toString());//输出一堆东西
System.out.println(calendar.get(Calendar.YEAR));
System.out.println(calendar.get(Calendar.MONTH)+1);//默认从0开始
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
设置指定时间的值
//设置指定时间的值
calendar.set(2022,10,11,11,11,11);
calendar.setTime(new Date(300000L));
calendar.setTimeInMillis(300000L);
calendar.set(Calendar.YEAR,2001);
System.out.println(calendar.get(Calendar.YEAR));//2001
LocalTime方法
//年月日 构造器私有 不让别人随意创建对象 只能使用他的方法 不能使用对象的东西 只能使用类的东西 静态的
LocalDate ld = LocalDate.now();
System.out.println(ld);
这里不可以创建new对象,对象没有方法,但是对象可以用localTime的方法
DateFormat方法
字符串类型格式化日期 现搜吧
数组类
对象数组
Student s1=new Student();
Student s2=new Student();
...
Student s1000=new Student();
有一千个同学要设置学校信息,如何让使用一个变量包含1000个同学
类型相同(基本/引用)-同学----->数组:既可以装学生也可以装老师 面向对象
存在堆里,由于对象在内存中是随机分布的,数组只保留对象的地址对象之间不是连续的

二维数组的动态内存分配
开始时,若第二维为空空,不分配地址设置为null。

后面给第二维数组赋值,同时把分配的地址写进一维数组

字符串相关类
String的构造方法超级多
但是用最基本的就行 也不用new 直接赋值字符串即可
对字符串的操作
大量api 记住十几个就行~
String str0="nickiMinaj";
String str1="ariannaGrande";
//常见方法
System.out.println(str0.charAt(0));//n
//字符串比较大小 负数小于 0等于 正数大于 字典顺序
System.out.println(str1.compareTo(str0));
//字符串拼接 需要一个返回值
String str2=str1.concat(str0);
System.out.println(str2);
//判断是否包含
System.out.println(str1.contains("anna"));
//是否为开始或者结尾
System.out.println(str0.startsWith("nicki")+"\t"+str0.endsWith("aj"));
//获取字节码形式
byte[] bytes=str0.getBytes();
for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i]+"\t");
}//110 105 99 107 105 77 105 110 97 106
//索引指定字符的位置 没找到就是-1
System.out.println(str1.indexOf("a"));
//是否为空
System.out.println("".isEmpty());//t
System.out.println(" ".isEmpty());//f
//java1.8没有isblank方法!!!
System.out.println("hello".length());//5
System.out.println("kim".replace("kim","west"));//普通替换
System.out.println("k1i23m34".replaceAll("\\d","-"));//k-i--m- 正则表达式的替换
//字符串的切分
String str3="a-b-c-d-e";
String[] strs=str3.split("-");//返回值是数组 什么都不写的话每个单词就是一个
System.out.println(strs.length);
System.out.println(Arrays.toString(strs));
//截取字符串 左闭右开
System.out.println(str0.substring(0,5));
//随地大小写
System.out.println(str0.toUpperCase(Locale.ROOT));//lower
//去掉前后空格
System.out.println(" n b ".trim());
//将基本数据类型转成字符串
System.out.println(String.valueOf('好'));

字符串常量池
(面试高频考点)
这六种情况的intern目前先不考虑,只考虑如下代码
System.out.println(str21==str22);
case1

即如果两个string字符串常量相等,那他们共享一个常量池中的地址(数据)如下 都指向0xm1

case2

即如果采用new方法的话 会现在堆里面创建一个对象 再检测一下方法区的常量池里面有没有 如果没有的话就建立一个 并且是可以被相同值所共享的 有的话就共享

System.out.println(str21==str22);//false 0xh1 0xh2

case3

常量折叠:编译期常量加减乘除的运算过程会被折叠
上面str32的常量在程序运行时会自动输出str3131而非占用两个方法区

System.out.println(str31==str32);//false 一个地址指向方法去 一个指向堆内存
case4

简而言之,只要new一次,就会创建一个对象,并且检查方法区是否有替身,有就不管,没有就创建。所以存在两个对象,方法区也存在两个变量。当这两个对象相加以后,新的对象可以理解为私生子,这个私生子不会有替身,因为程序不确定他是否会用到,就不给安排。

case5


case6


intern方法
当调用这个方法的时候,如果常量池中有与之相等的常量,则返回此常量的对象,如果没有,就把当前对象加到常量池并且返回堆当前对象的引用(地址!!!!!)即如果常量池没有这个字符串,就会存一个堆内存中字符串对象的引用。
public static void main(String[] args) {
//字面量判断
String str1 = "string1";//本身就在常量池里面了
System.out.println(str1 == str1.intern());//true
}

//创建对象
String str2 = new String("string2");
System.out.println(str2==str2.intern());//false
如果是new出来的他在栈里面指向的是堆内存(不管有没有在方法区的常量池创造对象),而intern指向的则是常量池

//创建对象--字符串
String str31 = new String("string3");
String str32="string3";
System.out.println(str31==str32);//f
System.out.println(str31.intern()==str32);//t

//创建对象
String str41=new String("string41")+new String("41");
System.out.println(str41.intern()==str41);//true

String str41=new String("string42")+new String("42");
String str42="string4242";
System.out.println(str41.intern()==str42);//t
String str51=new String("string52")+new String("52");
str51.intern(); //只要执行了intern,就会在常量池里面执行操作(创建0xm3指向0xh3)
String str52="string5252";//不会新建字符串常量了,因为发现0xh3指向的即为所得 把常量带到堆内存中
System.out.println(str51==str52);//true

调换顺序后则不相等
//调换顺序不等
String str51=new String("string52")+new String("52");
String str52="string5252";
str51.intern();
System.out.println(str51==str52);//false
总结
string创建对象的时候 如果是字面量 默认存常量池
如果是new 堆内存存一个 常量池建一个
字面量拼接===>字面量拼接 常量折叠 直接存常量池
如果new拼接=====>堆new几个 常量池对应几个 但是拼接后的孩子不管 只放在堆里面
字面量+new =====>堆里面两个 原来的new和新孩子 常量池两个 原来的字面量和原来new对象对应的常量池
intern总结
目的 将字符串存到常量池(堆内存的引用放到常量池 对象的复用)
如果字符串本来就在常量池 返回当前地址
如果字符串在堆内存
常量池 有 返回已有地址
常量池 无 在常量池创建一个新的!但并不存值 只放堆内存对象的地址
源码分析


StringBuffer&StringBuilder
均代表可变字符序列 而且方法也一样
StringBuilder 更快(在大多数类里面)
虽然StringBuffer更慢但是更安全(多线程)
绝大部分情况还是以String为主 如果多字符串拼接可考虑StringBuffer&StringBuilder
总结
面试总考内存分析 必须学会

Math类

File类
Enum类
本质上是一种约束 划定一个有界的范围 用范围里的数据表示状态
eacher lisa=new teacher(1,"lisa",type.BITCH);
teacher nicki=new teacher(1,"lisa",type.BITCH);
System.out.println(lisa.teachertype==nicki.teachertype);//true
//枚举对象不论被使用多少次 内存中只会被创建一次 并且是随着枚举类的加载一次性全部被创建
比如
type(int salary, int vacation) {//构造函数
this.salary = salary;
this.vacation = vacation;
System.out.println(salary);//对一个对象使用枚举类 此时会输出四个salary因为只要加载枚举类就会把全部的等级都加载出来
}
当我把枚举类内部变量设置成private 可以采用get方法获得此变量的值
enum type{
LOW(1000,1),MEDIUM(2000,2),HIGH(3000,3),BITCH(4000,4);
private int salary;
private int vacation;
type(int salary, int vacation) {//构造函数
this.salary = salary;
this.vacation = vacation;
System.out.println(salary);//对一个对象使用枚举类 此时会输出四个salary因为只要加载枚举类就会把全部的等级都加载出来
}
public int getSalary() {
return salary;
}
public int getVacation() {
return vacation;
}
}
枚举类内存模型

无论其他多少个对象都有excellent,他们都指向方法区位的的字节码,此字节码引用堆的对象
二分查找的前提
有序

浙公网安备 33010602011771号