面向对象基础篇 – 常用类解析
Object类
Object类是所有类的最终父类,也就是说,我们定义一个类,哪怕不写extends Object ,此时这个类也是默认继承Object的,
并且可是使用继承来的方法
class Demo{
}
class Test{
public static void main(string[] args){
Demo demo = new Demo();
System.out.println(demo.hashCode());
//可以看到,尽管我们在Demo类中并没有定义hashCode方法,但我们依旧可以使用
//这是因为,我们继承了Object类,而Object类中是存在这个方法的
}
}
Object类常用方法解析
- native void registerNatives()
private static native void registerNatives();
static {
registerNatives();
//本地方法 主要用于在内存中注册对象信息 用于创建对象使用的
}
- getClass
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
//本地方法 获取JVM运行时的字节码文件对象 反射知识中会学到
class Test{
public static void main(string[] args){
Object demo = new Object();
System.out.println(demo.getClass());
//输出 class java.lang.Object
//该方法不能被重写
}
}
- hashCode()
@HotSpotIntrinsicCandidate
public native int hashCode();
//本地方法 获取对象的哈希值-作为元素在哈希表中存储位置 默认内容 就是对象在堆内存中的地址
/*
子类可以重写 在哈希表中 子类的哈希值应该由子类的内容来决定
为什么?因为如果不去按照子类的内容来定义哈希值 哈希值默认就是对象的地址
任何一个对象的地址都是不一样的,在哈希表中就不会产生哈希冲突,退化成了纯粹的一维数组
*/
//例如,学生类,用学号代表哈希码
class Student{
long studyNum = 20408030314 ;
@Override
public int hashCode() { //根据类自身的内容来计算哈希值
return studyNum;
}
}
- equals()
public boolean equals(Object obj) {
return (this == obj);
}
/*
对比当前 对象this 与 传入对象obj 的相等性
Object中 默认比两个对象的地址 == 其实比的就是两个变量空间中的内容
基本数据类型 == 比的就是常量值
引用数据类型 == 比的就是地址值
子类重写时,应该按照子类的内容来进行比较!
*/
//不重写
class Demo{
int num ;
public Demo(int num){
this.num = num ;
}
}
class Test{
public static void main(String[] args){
Demo demo1 = new Demo(10);
Demo demo2 = new Demo(10);
System.out.println(demo1.equals(demo2))//输出false
//在没有重写equals方法的情况下,比较的是两个对象的存储地址
//虽然demo1和demo2的num值都是10,但他们是两个独立的对象,存储地址是不同的,因此返回false
}
}
//重写
//不重写
class Demo{
int num ;
public Demo(int num){
this.num = num ;
}
public boolean equals(Object obj) {
return (this.num == obj.num);
}
}
class Test{
public static void main(String[] args){
Demo demo1 = new Demo(10);
Demo demo2 = new Demo(10);
System.out.println(demo1.equals(demo2))//输出true
//此时,我们重写了equals方法,让他比较的是每个对象的num属性。而num又是基本数据类型,并且值相等,因此返回true
}
}
- toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
//默认返回类名+@+地址值的十六进制形式
}
//重写
class Demo{
String name ;
int age ;
public Demo(String name,int age){
this.name = name ;
this.age = age ;
}
public String toString(){
return "Demo" + "{name : " + name + "age :"+age+"}";
}
}
class Test{
public static void main(String[] args){
Demo demo = new Demo("小明",10);
System.out.println(demo);//此时输出对象,默认调用他的toString方法 ,
//输出 Demo { name : 小明 age : 10 }
}
}
- clone()
protected native Object clone() throws CloneNotSupportedException;
//做一个该对象的副本/拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //浅克隆 仅仅把这个对象的属性重新进行复制而已
//基本数据类型直接复制即可 name age互不相扰
//引用数据类型直接复制是不行的
//需要把引用数据类型再次进行克隆 ——深克隆
//如果克隆层次较深 上述方法就显得太复杂了!
}
class Car implements Cloneable{
String band;
public Car(String band) {
this.band = band;
}
@Override
public String toString() {
return band;
}
@Override
protected Object clone() throws CloneNotSupportedException {
rsheneturn super.clone();
}
}
//一个类实例的对象如果要被克隆,则这个类要实现Cloneable接口
//该接口中并未任方法。只是作为一个可以被克隆的标记
class Demo implements Cloneable{
String name ;
int age ;
Car car ;
@Override
protected Object clone() throws CloneNotSupportedException {
//return super.clone(); //浅克隆 仅仅把这个对象的属性重新进行复制而已
Object obj = super.clone();
Demo newDemo = (Demo) obj;
other.car = (Car) car.clone(); //深克隆,对引用属性再次克隆
return other;
}
}
基本数据类型包装类
有缓存,-128 ~127
小数点没有缓存
有缓存, 0 ~ 128
无缓存
class Demo{
public static void main(String[] args){
int i1 = 10;
Integer i2 = 10 ;
Integer i3 = new Integer(10);
System.out.println(System.identityHashCode(i1));
System.out.println(System.identityHashCode(i2));
//发现 i1 和 i2 的 哈希值是一样的
System.out.println(System.identityHashCode(i3));
//i3 的哈希值 和他们是不一样的
System.out.println(i1 == i2); //true
System.out.println(i2 == i3); //false
System.out.println(i3 == i1); //true
/*
这是因为, 在基本数据类型和基本数据包装类比较时,其实包装类会默认拆分
i1 == i2 等价于 i1 == i2.intValue();
intValue()方法返回的是对象的value,也就是10 ,因此 true
而包装类型和包装类型比较时,默认比较的是 对象的地址
因此 i2 == i3 输出 false
*/
System.out.println(i1 + i2);//20
System.out.println(i2 + i3);//20
/*
可见,包装类型 或 基本类型 在与 包装类型 进行运算时,也会自动拆分
i1 + i2 等价于 i1 + i2.intValue();
i2 + i3 等价于 i2.intValue()+i3.intValue();
}
}
class Demo{
public static void main(String[] args){
Integer i1 = 10 ;
Integer i2 = Integer.valueOf(10);
/*直接让Integer 对象 = 一个数值
就相当于 调用了Integer.valueOf(int i);方法
输出 两个的哈希值,我们发现是一样的
*/
}
// valueOf方法
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//其中 ,IntegerCache.low = -128 ;IntegerCache.high = 127
/*
也就是说,如果,给定值在这个范围之间,是不会创建对象的,而是直接在,缓存中提取复用
*/
}
常用方法
- parseInt() 字符串 转整数,默认10进制
class Demo{
public static void main(String[] args){
String s = "123";
int i = Integer.parseInt(s);//10进制123
System.out.println(Integer.parseInt("10",2));//2进制的10 ,1010
}
}
- toBinaryString(int i) 转二进制字符串
- toHexString(int i) 转16进制
- toOctalString(int i) 转8进制
class Demo {
public static void main(String[] args){
int i = 10 ;
System.out,println(Integer.toBinaryString(i));//1010
System.out,println(Integer.toHexString(i));//a
System.out,println(Integer.toOctalString(i));//12
}
}
- reveser(int i) ;字节反转
class Demo{
public static void main(String[] args){
System.out.println(Integet.reveser(123456));//38240256
}
}
- Character.is…();判断是否是什么
public class Demo {
public static void main(String[] args) {
System.out.println(Character.isDigit('8')); //是不是数字 true
System.out.println(Character.isLetter('5'));//是不是字母 false
//等等...
}
}
String 和 StringBuilder 和 StringBuffer
String
public final class String 证明String不能被继承
implements java.io.Serializable, Comparable, CharSequence
Serializable表示字符串可以被序列化存储到硬盘
Comparable 表示字符串自身具有可比较的功能
CharSequence 表示字符串是一个字符序列
- private final char value[];
字符串本身就是一个字符数组(JDK8之后是字节数组) 数组本身长度一旦确定,则不可改变
数组中的元素可以改变的
final 数组的对象不能改变 但是内部的元素依旧可以改变
private 禁止外界访问
目前来看String中的value外界不可访问,在内部value是对象不可变的,但是并没有强制要求内容不可变
只不过是对字符数组的操作 尤其是在修改之类的操作时 都是另创建数组或字符串操作的 并没有在原数组上进行
关于字符常量池String Table
该常量池会存储代码中直接出现的那些字符序列
class Dmeo{
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "World";
String s3 = "Hello" + "World";
String s4 = "HelloWorld";
//以上直接出现的字符串,会存储在字符常量池中,如果后续出现,则会继续复用
String s5 = new String("HelloWorld");
System.out.println(s4 == s5);//false
//而对于new 的字符串对象,他里面的value数组存储的就是 常量池中已存在的地址
String s6 = s1 + s2;
String s7 = s1 + "World";
System.out.println(s4 == s7);//false
//使用intern函数,可以获取它在常量池中的地址
System.out.println(s4 == s7.intern());//true
//intern的含义就是将字符串对象中的value装载都常量池中 并返回其地址
//如果池中已经存在,则直接复用,否则新建
}
}
常用方法
- charAt(int index) 返回指定索引的字符
public class Demo {
public static void main(String[] args) {
String str = "123456";
System.out.println(str.charAt(2));//输出3
}
}
- length()返回字符串的长度
public class Demo {
public static void main(String[] args) {
String str = "123456";
System.out.println(str.length()); //6
}
}
- compareTo(),按字典序比较两个字符串
public class Demo {
public static void main(String[] args) {
String str = "123456";
String str2 = "234567";
System.out.println(str.compareTo(str2)); //-1 (1-2)
}
}
- equals();比较两个字符串的具体内容
public class Demo {
public static void main(String[] args) {
String str = "123456";
String str2 = "234567";
System.out.println(str.equals(str2));
}
}
- indexOf();返回指定字符在字符串中第一次上出现索引
public class Demo {
public static void main(String[] args) {
String str = "123456";
String str2 = "23";
System.out.println(str.indexOf(str2)); //1
}
}
- startsWith();是否以指定字符或字符串开头
- endsWith();是否以指定字符或字符串结尾
public class Demo {
public static void main(String[] args) {
String str = "23456";
String str2 = "23";
System.out.println(str.startsWith(str2)); //true
System.out.println(str.endsWith("6")); //true
}
}
-
subString (),返回一个子序列
-
matches();是否符合正则表达式
-
replace();替换字符
-
toUpperCase();转大写
-
toLowerCase();转小写
-
tirm();返回去掉首尾空白符的字符串
StringBuilder
一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。
该类被设计用作 StringBuffer 的一个简易替换,
用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。
如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
extends AbstractStringBuilder implements Appendable, CharSequence
经过对AbstractStringBuilder的观赏 发现 对字符串的操作都是在value数组上进行的
- 特点
- 可变的字符序列,本质上是一个可修改可变长的字符数组而已
- 字符缓冲区
- 线程不安全
- 单线程模式下速度较快
public class Demo {
public static void main(String[] args) {
//拼接字符串
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
String s = "";
s +='[';
for (int i = 0; i < arr.length; i++) {
s += arr[i];
if (i == arr.length - 1) {
s += ']';
} else {
s += ',';
s += ' ';
}
System.out.println(s);
}
}
}
/*
输出
[1,
[1, 2,
[1, 2, 3,
[1, 2, 3, 4,
[1, 2, 3, 4, 5,
[1, 2, 3, 4, 5, 6,
[1, 2, 3, 4, 5, 6, 7,
[1, 2, 3, 4, 5, 6, 7, 8,
[1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
/*
上面说到,s += "";的过程是创建对象的,所以,普通的字符串拼接会造成创建大量的中间对象,字符串常量,造成空间浪费
而StringBuilser 则有效解决了这个问题
*/
class Demo{
public static void main(String[] args) {
//拼接-> [1,2,3,4,5,6,7,8,9]
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
StringBuffer s = new StringBuffer();
s.append('[');
for (int i = 0; i < arr.length; i++) {
s.append(arr[i]);
if (i == arr.length - 1) {
s.append(']');
} else {
s.append(',');
s.append(' ');
}
}
System.out.println(s.toString());
}
}
/*
输出
[1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
常用方法
- append();追加
public class Demo {
public static void main(String[] args) {
StringBuilder str = new StringBuilder("");
str.append(5);
str.append("我");
System.out.println(str);
}
}
// 输出 5我
- setCharAt();修改指定位置的字符
public class Demo {
public static void main(String[] args) {
StringBuilder str = new StringBuilder("dsdaf");
str.setCharAt(0,'k');
System.out.println(str);
}
}
//输出 ksdaf
- delete();删除指定位置的字符串
public class Demo {
public static void main(String[] args) {
StringBuilder str = new StringBuilder("dsdaf");
str.delete(0,2);
System.out.println(str);
}
}
//输出 daf
- insert();在指定位置插入字符序列
public class Demo {
public static void main(String[] args) {
StringBuilder str = new StringBuilder("dsdaf");
str.insert(0,'5');
System.out.println(str);
}
}
//输出 5dsdaf
- reverse();字符串翻转
public class Demo {
public static void main(String[] args) {
StringBuilder str = new StringBuilder("dsdaf");
System.out.println(str.reverse());
}
}
//输出 fadsd
- trimToSize() 剔除未使用空间
StringBuffer
StringBuffer 是线程安全的 适用于多线程的情况下
和StringBuilder来比较的话 StringBuffer中大部分方法都有synchronized修饰
synchronized同步的De
BigInteger 和 BigDecimal
在Java中 long与double,是最长的基本数据类型
而对于超出long与double所能承受的最大范围时,我们可以使用 BigInteger 和 BigDecimal
无论是BigInteger还是BigDecimal,它们的本质其实就是数组
后续的四则运算其是都是基于数组进行的四则运算
常用方法
- add();加
- subtract();减
- multiply();乘
- divide();除
- remainder();取余
public class Demo {
public static void main(String[] args) {
BigInteger i1 = new BigInteger("123");
BigInteger i2 = new BigInteger("456");
System.out.println(i1.add(i2));
System.out.println(i1.subtract(i2));
System.out.println(i1.multiply(i2));
System.out.println(i1.divide(i2));
System.out.println(i1.remainder(i2));
}
}
/*
输出
579
-333
56088
0
123
*/
- 转基本类型
public class Demo {
public static void main(String[] args) {
BigInteger i1 = new BigInteger("123");
BigInteger i2 = new BigInteger("456");
long num = i1.longValue();//精确转换
long num2 = i2.longValueExact();//不精确转换,超出范围会抛出异常
}
}
- BigDecimal
其实就是 BigInteger + scale
scale 小数位个数
时间类
时间戳
从1970年1月1日00:00开始至今所有的毫秒值
System.currentTimeMillis();//获取时间戳
java.util.Date 日期类
java.sql.Date 子类 年月日
java.sql.Time 子类 时分秒
java.sql.Timestamp 子类 年月日时分秒
java.util.Calendar 日历类
java.text.DataFormat 日期格式化类
java.text.SimpleDateFormat 子类
日期
public class Demo {
public static void main(String[] args) {
java.util.Date date1 = new java.util.Date();
//根据当前时间戳创建对象
java.sql.Date date2 = new java.sql.Date(date1.getTime());
java.sql.Time date3 = new java.sql.Time(date1.getTime());
java.sql.Timestamp date4 = new java.sql.Timestamp(date1.getTime());
System.out.println("java.util.Date : "+date1);
System.out.println("java.sql.Date : "+date2);
System.out.println("java.sql.Time : "+date3);
System.out.println("java.sql.Timestamp : "+date4);
}
}
/*
输出
java.util.Date : Thu Oct 13 19:38:33 CST 2022
java.sql.Date : 2022-10-13
java.sql.Time : 19:38:33
java.sql.Timestamp : 2022-10-13 19:38:33.193
*/
//CST可视为中国、美国、澳大利亚、古巴的时区
//GMT 格林尼治标准时间
//UTC 统一的标准时间
java.util.Date
public class Demo {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();//默认当前时间戳创建对象
java.util.Date date1 = new java.util.Date(10000L);//指定时间戳创建对象
System.out.println(date);
//Thu Oct 13 20:13:17 CST 2022
System.out.println(date1);
//Thu Jan 01 08:00:10 CST 1970
System.out.println(date.getTimezoneOffset());//
//-480
//返回与格林尼治时间的时差,以分钟计时的,正好8个小时
//输出-480 意味着北京时间-480分钟等于GMT时间
System.out.println(date.getTime());
//1665664427912
//返回时间戳
System.out.println(date.after(date1));
//true
System.out.println(date.before(date1));
//false
//比较的是毫秒值
}
}
- toString
public String toString() {
// "EEE MMM dd HH:mm:ss zzz yyyy";
BaseCalendar.Date date = normalize();
StringBuilder sb = new StringBuilder(28);
int index = date.getDayOfWeek();
if (index == BaseCalendar.SUNDAY) {
index = 8;
}
convertToAbbr(sb, wtb[index]).append(' '); // EEE
convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' '); // MMM
CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd
CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':'); // HH
CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
TimeZone zi = date.getZone();
if (zi != null) {
sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
} else {
sb.append("GMT");
}
sb.append(' ').append(date.getYear()); // yyyy
return sb.toString();
}
/*
可以看到 默认输出
"EEE MMM dd HH:mm:ss zzz yyyy";
周 月 日 时 分 秒 时区 年
如何按照自己的行为习惯输出,看下面的DateFormat类
*/
日历
public class Demo {
public static void main(String[] args) {
java.util.Calendar calendar = java.util.Calendar.getInstance();
System.out.println(" java.util.Calendar :" +calendar.getTime());
}
}
/*
输出
java.util.Calendar :Thu Oct 13 19:46:09 CST 2022
*/
java.text.DataFormat 日期格式化类
自定义时间格式
可以将 text 转化为 date
也可以将 date 转化为 text
//date -》text
public class Demo {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
java.text.SimpleDateFormat simpleDateFormat = new java.text.SimpleDateFormat();
String str = simpleDateFormat.format(date);
System.out.println(str);
//2022/10/13 下午8:56 默认输出 年/月/日 时辰时间
}
}
//text -> date
public class Demo {
public static void main(String[] args) throws ParseException {
java.text.SimpleDateFormat simpleDateFormat = new java.text.SimpleDateFormat();
String str2 = "2011/10/1 上午5:31";
java.util.Date date1 = simpleDateFormat.parse(str2);
System.out.println(date1);
//Sat Oct 01 05:31:00 CST 2011
}
}
//自定义格式
//date -> text
public class Demo {
public static void main(String[] args) throws ParseException {
java.text.SimpleDateFormat simpleDateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm");
java.util.Date date1 = new java.util.Date();
String str = simpleDateFormat.format(date1);
System.out.println(str);
//2022-10-13 21:10
}
}
//自定义格式
//text->date
public class Demo {
public static void main(String[] args) throws ParseException {
java.text.SimpleDateFormat simpleDateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Date date = simpleDateFormat.parse("2022-10-07 16:21:59") ;
System.out.println(date);
//Fri Oct 07 16:21:59 CST 2022
}
}
字母含义
y 年 yyyy 2022 yy 22 M 月 MM 07 w 周 ww 27 W 月份中的周数 D 年中的第几天 d 月中的第几天 F 月份中星期 E 星期中的天数 H 小时0-23 k 小时数1-24 K 小时数0-11 h 小时数1-12 m 分钟数 s 秒数 S 毫秒数 z 时区 Z 时区
常见的时间格式
//常见的时间格式
public class DateTest04 {
public static void main(String[] args) throws ParseException {
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = dateFormat.parse("2015-08-08 15:30:31");
System.out.println(date);
}
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-M-d");
Date date = dateFormat.parse("2015-12-12");
System.out.println(date);
}
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-M-d H:m:s");
Date date = dateFormat.parse("2015-12-12 18:8:30");
System.out.println(date);
}
{
SimpleDateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy h:m:s aa", Locale.ENGLISH);
Date date = dateFormat.parse("Aug 28, 2015 6:8:30 PM");
System.out.println(date);
}
}
}