1、Object类
- 所有类的父类
- 因为所有类都继承了Object类,因此省略了extends Object关键字
- Object中的属性和方法就具有通用性
- Object只声明了一个空参构造器
主要方法
- toString()
- getClass()
- equals()
- clone()
- finalize()
- 注意:Object类中的getClass(),notify(),notifyAll(),wait()等方法被定义为final类型,因此不能重写
clone()
- 复制对象
- 先分配一个和源对象同样大小的空间,在这个空间中创建一个新对象
Java语言中创建对象的方法
- 使用New操作符创建一个对象
- 使用clone方法复制一个对象
Clone和new创建对象的区别
- new:本意是分配内存。首先根据new操作符后面的类型分配对应大小的内存空间,在调用构造函数,填充对象的各个域(即初始化)。调用完构造方法后,一个对象创建完毕。将其引用(地址)发布到外部,即可在外部使用。
如 Student stu = new Student();
new Student()即创建对象,Student stu代表用一个Student类型的对象接收创建好的新对象返回的地址- clone():
- 第一步和new类似,都是分配内存。
- 调用clone()方法时,分配的内存和源对象(即调用clone方法的对象)相同
- 然后再使用源对象中对应的各个域,填充新对象的域。填充完成后,clone方法返回,一个新的相同的对象被创建。
- 简单来说,就是通过clone方法创建一个和源对象类型相同的对象,按照源对象的规范要求新对象
- 注意:调用clone()方法的对象必须实现Cloneable接口,重写clone()方法
- Clone()方法是被protected的,保证只有类里面才能克隆该类对象
Clone与copy的区别
- copy只是简单的指向了同一片内存地址,修改其中的属性时两个对象都会受到影响
Studentstu=newStudent();
Studentxiaoming=stu; // 指向同一片地址
xiaoming.setAge(18); // stu的age受到影响也变成了18
- clone:完全拷贝出一个地址不同的对象,修改时不会受到影响
Studentstu=newStudent();
//需要先实现接口
Studentxiaoming= (stu).clone(); // 生成一个和stu一样的方法,且指向不同地址
xiaoming.setAge(18); // stu的age不受影响
Shallow Clone浅克隆和Deep Clone深克隆
- 浅克隆:直接复制(让新对象的 属性/对象 指向源对象 属性/对象 的地址)
- 深克隆:再创建一个新的、相同的属性/对象给新对象(地址不同)
- clone方法执行的是浅拷贝(整体),如果有需要深拷贝的地方就要这个被引用的对象(部分)实现Cloneable接口并重写clone方法
类名 对象名 = (类名)super().clone()
使用Cloneable接口的clone()方法深拷贝
2、toString()方法
- 返回一个字符串,由类名、“@”和此对象哈希码的无符号16进制组成
publicStringtoString() {
returngetClass().getName() +"@"+Integer.toHexString(hashCode());
}
- 当我们输出一个对象的引用时,实际上调用的是当前对象的toString()
System.out.println(cust.toString);
System.out.println(cust);
- 此方法用的比较多,一般子类(String、Date、File等)都有覆盖(用于输出字符串值)
- 推荐在学习阶段,所有属性的类都加上toString()方法
3、getClass()方法
- 返回Object的运行类型
- 不可重写,要调用的话,一般和getName()联合使用,如
getClass().getName()
publicstaticvoidmain(String[] args) {
Objecto=newObject();
System.out.println(o.getClass().getName()); // java.lang.Object
System.out.println(o.getClass()); // class java.lang.Object
}
4、finalize()方法
- 用于释放资源,由于无法确定该方法什么时候被调用,很少使用
- 工作原理:
- 垃圾回收器准备好释放对象占用的存储空间,首先调用finalize()方法
- 并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存
- 用途:
- 无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存
- 这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配了存储空间
- 不过这种情况一般发生在使用”本地方法“的情况下,本地方法是一种在Java中调用非Java代码的方式
垃圾回收
- 对象可能不被垃圾回收。只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放
- 垃圾回收并不等于”析构“
- 析构函数:与构造函数相反,用于在对象调用完毕时回收(delete)对象开辟(new)的存储空间
- 垃圾回收只与内存有关。使用垃圾回收的唯一原因是为了回收程序不再使用的内存
5、equals()方法
- 判断两个对象是否相等(判断两个对象引用指向的是否为同一个对象,即*内存地址*是否相同)
object.equals(Object obj 要比较的对象)
- 注意:
- 如果子类重写equals()方法,就需要重写hashCode()方法,比如String类就重写了equals()方法,同时也重写了hashCode()方法
- 即便是内容完全相同的两块不同内存的对象,也返回false
==和equals()的区别
==
- 前提条件:比较两边的类型要一致,不然编译会报错
- 对于基本数据类型:比较两个数据的值是否相等
- 对于引用数据类型:比较两个对象地址值是否相等
equals
- 是一个方法,只适用于引用数据类型
- Object类中equals()的定义:和==的作用相同(比较两个对象的地址值是否相等)
- 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相等,而是比较两个对象的“实体内容”是否相同
- 通常情况下,我们自定义类使用equals()的目的是比较两个对象的"实体内容"是否相等,那么就需要手动重写(可以在IDE中使用快捷键自动重写)
6、hashCode()方法
- 返回该对象的哈希值(一个整数,表示在哈希表中的位置)
- 两个对象相等他们的哈希值一定相等,反之则未必(相同的哈希值下可能有多个值,那些值之间不一定相等)
- 要判断两个值是否真正相等需要使用equals()方法
7、notify()方法
- 唤醒在该对象上等待(wait()方法)的某个线程(随意)
- notifyAll():唤醒在该对象上等待的所有线程
8、wait()方法
- 让线程进入等待状态,直到其他线程调用此对象的notify()方法或notifyAll()方法
- 当前线程必须是此对象的监视器所有者,否则会发生IllegalMonitorStateException 异常。
- 如果当前线程在等待之前或在等待时被任何线程中断,则会抛出 InterruptedException 异常
- timeout 要等待的最长时间(单位:毫秒)
- nanos 额外时间(单位:微妙)
2、包装类
- 因为实际开发过程中经常需要操作对象,而不是普通数据类型的情形
- 把8种基本类型包装成面向对象的类
- 每个包装类的对象封装一个基本的数据类型,里面提供一些方法
- 注意:基本数据类型的初始值是0,0.0,falese.... 包装类的初始值是null
- 每一个包装类都是Number的子类(除了boolean和Character)
- 基本类型和包装类可以相互转换:
- 基本类型 -> 对应的包装类 装箱 (构造方法)
- 包装类 -> 基本类型 拆箱 XXXValue()方法
// int 和 Integer相互转换
publicstaticvoidmain(String[] args) {
intm=500;
Integerobj=newInteger(m); // 手动装箱
intn=obj.intValue(); // 手动拆箱
System.out.println("n = "+n);
Integerobj1=newInteger(500);
System.out.println("obj 等价于 obj1?"+obj.equals(obj1));
}
Integern=newInteger("123"); // 输入纯数字组成的字符串时可以转换成对应的类型
// 输入带字符的String时会报错
Booleanb1=newBoolean("TRue"); //True 在忽略大小写的情况下
Booleanb2=newBoolean("true123"); // false 但不会报错
- 自动装箱和自动拆箱
- JDK1.5之前只能手动
- JDK1.5之后可以自动装箱
publicstaticvoidmain(String[] args) {
intm=500;
Integerobj=m; // 自动装箱
intn=obj; // 自动拆箱
System.out.println("n = "+n);
Integerobj1=500;
System.out.println("obj 等价于 obj1?"+obj.equals(obj1));
}
- 这些操作其实都是编译器根据你所编写的语法,决定是否进行操作的
基本数据类型+包装类和String的相互转化
// 将字符串转换成整数 paseInt(String s, int radix);
// s为要转换的字符串,radix为进制(可选)
publicvoidtest(){
Stringstr1="123";
intnum2=Integer.parseInt(str1);
// 调用对应包装类的parseXXX方法
System.out.println(num2);
}
// 将整数转换成字符串 toString()方法
// 或者直接在整数后面加空字符串即可
// 或调用String的VauleOf()
publicstaticvoidmain(String[] args){
intm=500;
Strings=Integer.toString(m); // 方式三:调用包装类的toString()方法将包装类转换成String
Strings2=m+""; // 方式一:连接运算 + ” “
System.out.println("s= "+s);
Doubled1=newDouble(12.4);
Doubled2=10.1;
Strings3=String.valueof(d1);
Strings4=String.valueof(d2); // 方式二:调用String的VauleOf() 包装类和基本数据类型都适用
}
面试题
编译的时候要求两边的数据类型一致,所以统一成Double类型
Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[]
保存了从-128 ~ 127之间的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在-128 ~ 127范围内时,可以直接使用数组中的元素,不用再去New了,目的是为了提高效率
3、Math类
- Math.PI——圆周率
- Math.E——e的常量
- Math.abs——求绝对值
- Math.sin/asin/cos/acos/tan/atan/atan2 正弦/反正弦/余弦/反余弦/正切/反正切/商的反正切
- Math.toDegrees 弧度转化角度 Math.toRadians 角度转化为弧度
- Math.ceil——返回这个数四舍五入(入)后的最大值 double类型
System.out.println(Math.ceil(-10.1));//-10.0
System.out.println(Math.ceil(10.7)); //11.0
- Math.floor——返回这个数四舍五入(舍)后的最小值
System.out.println(Math.ceil(-10.1));//-11.0
System.out.println(Math.ceil(10.7)); //10.0
- Math.IEEEremainder——求余
- Math.max/min——最大最小
- Math.sqrt——计算平方根
- Math.cbrt——计算立方根
- Math.pow(num,i)——求n的i次方
- Math.exp——求e的任意次方
- Math.log10——以10为底的对数
- Math.rint——四舍五入,返回double
- .5的时候会取偶数
System.out.println(Math.rint(10.1)); //10.0
System.out.println(Math.rint(10.7)); //11.0
System.out.println(Math.rint(11.5)); //12.0 取偶数
System.out.println(Math.rint(10.5)); //10.0 取偶数
System.out.println(Math.rint(10.51)); // 11.0
System.out.println(Math.rint(10.5)); // -10.0 取偶数
- Math.round——四舍五入,float时返回int型,double时返回long型
System.out.println(Math.round(10.51)); //11
// 注意: 大于五全部加,等于五正数加,小于五全不加
System.out.println(Math.round(-10.5)); //-10
System.out.println(Math.round(-10.51)); //-11
System.out.println(Math.round(-10.2)); // -10
- Math.random——返回0,1之间的随机数 double
4、Random类
java.lang.Math.Random
- 调用Math.Random()可以返回带正号的double值,该值[0.0,1.0)
publicstaticvoidmain(String[] args) {
// 结果是个double类型的值,区间为[0.0,1.0)
System.out.println("Math.random()="+Math.random());
intnum= (int) (Math.random() *3);
// 取值范围变成了[0,3)
// 注意不要写成(int)Math.random()*3,这个结果为0或1,因为先执行了强制转换
System.out.println("num="+num);
}
//结果
//Math.random()=0.44938147153848396
//num=1
java.util.Random类
- 可以产生boolean、int、long、double、byte数组的随机数
- 重子数相同的Random对象生成的随机数序列一样
- Random():创建一个新的随机数生成器,使用一个和系统时间相对应的数字作为种子数
- Random(long seed):使用单个long种子创建一个新的随机数生成器(在创建对象的时候可以给定任意一个合法的种子数,种子数只是随机算法的起源数字,和生成的随机数的区间没有任何关系)
publicstaticvoidmain(String[] args) {
Randomrand=newRandom();
inti=rand.nextInt(100); // 随机生成[0,100)的int整数
System.out.println(i);
}
- nextDouble() nextInt() nextBoolean() nextLong() nextFloat()等方法同理
- nextBytes(byte[] bytes):生成随机字节并将其置于用户提供的 byte 数组中。
publicstaticvoidmain(String[] args) {
Randomran1=newRandom(25);
System.out.println("使用种子为25的Random对象生成[0,100)内随机整数序列: ");
for (inti=0; i<10; i++) {
System.out.print(ran1.nextInt(100) +" ");
}
System.out.println();
// 使用相同种子的Random对象,生成的随机数序列一样
Randomran2=newRandom(25);
System.out.println("使用种子为25的Random对象生成[0,100)内随机整数序列: ");
for (inti=0; i<10; i++) {
System.out.print(ran2.nextInt(100) +" ");
}
System.out.println();
}
void setSeed(long seed)
重新设置一个long类型的Random对象的种子数
5、时间日期类
1、Date类
Date()
使用日期和时间初始化对象Date(long millisec)
millisec是从1970年1月1日起的毫秒数
publicstaticvoidmain(Stringargs[]) {
// 初始化 Date 对象
Datedate=newDate();
// 使用 toString() 函数显示日期时间
System.out.println(date.toString());
//Sat Apr 27 15:09:43 CST 2019
}
publicstaticvoidmain(String[] args) {
// 初始化 Date 对象
Datedate=newDate();
longtime=date.getTime();
longtime2=date.getTime(); // 获取时间
System.out.println(time==time2);
}
// 比较哪个在前
publicstaticvoidmain(String[] args) {
booleanbefore=newDate(97, 01, 05).before(newDate(99, 11, 16));
System.out.println(before);
}
publicstaticvoidmain(Stringargs[]) {
DatedNow=newDate( );
SimpleDateFormatft=newSimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: "+ft.format(dNow));
}
- boolean after(Date date) 若当调用此方法的Date对象在指定日期之后返回true,否则返回 false。
- boolean before(Date date) 若当调用此方法的Date对象在指定日期之前返回true,否则返 回false。
- Object clone( ) 返回此对象的副本。
- int compareTo(Date date) 比较当调用此方法的Date对象和指定日期。两者相等时候返回 0。调用对象在指定日期之前则返回负数。调用对象在指定日期之后则返回正数。
- int compareTo(Object obj) 若obj是Date类型则操作等同于compareTo(Date) 。否则它抛 出ClassCastException。
- boolean equals(Object date) 当调用此方法的Date对象和指定日期相等时候返回true,否 则返回false。
- long getTime( ) 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒 数。
- int hashCode( ) 返回此对象的哈希码值。
- void setTime(long time) 用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日 期。
- String toString( ) 把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
2、simpleDateFormat 格式化日期
import java.util.*;
importjava.text.*;
publicclassDateDemo {
publicstaticvoidmain(String[] args) {
DatedNow=newDate( );
SimpleDateFormatft=newSimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: "+ft.format(dNow));
}
}// 当前时间为: 2018-09-06 10:16:34
- 注意大小写为不同的含义,如MM是月份,mm是分 HH是24小时制 hh是12小时制
时间和日期的格式化编码
字母 | 描述 | 示例 |
G | 纪元标记 | AD |
y | 四位年份 | 2001 |
M | 月份 | July or 07 |
d | 一个月的日期 | 10 |
h | A.M./P.M. (1~12)格式小时 | 12 |
H | 一天中的小时 (0~23) | 22 |
m | 分钟数 | 30 |
s | 秒数 | 55 |
S | 毫秒数 | 234 |
E | 星期几 | Tuesday |
D | 一年中的日子 | 360 |
F | 一个月中第几周的周几 | 2 (second Wed. in July) |
w | 一年中第几周 | 40 |
W | 一个月中第几周 | 1 |
a | A.M./P.M. 标记 | PM |
k | 一天中的小时(1~24) | 24 |
K | A.M./P.M. (0~11)格式小时 | 10 |
z | 时区 | Eastern Standard Time |
' | 文字定界符 | Delimiter |
" | 单引号 | ` |
使用printf格式化日期
%t
开头+下面表格中的字母结尾
转 换 符 | 说 明 | 示 例 |
c | 包括全部日期和时间信息 | 星期六 十月 27 14:21:20 CST 2007 |
F | "年-月-日"格式 | 2007-10-27 |
D | "月/日/年"格式 | 10/27/07 |
r | "HH:MM:SS PM"格式(12时制) | 02:25:51 下午 |
T | "HH:MM:SS"格式(24时制) | 14:28:16 |
R | "HH:MM"格式(24时制) | 14:28 |
importjava.util.Date;
publicclassDateDemo {
publicstaticvoidmain(String[] args) {
// 初始化 Date 对象
Datedate=newDate();
// %t 格式化 %n换行输出
// %n只能用在printf语句中输出,而\n可以用在所有语句中输出
//c的使用
System.out.printf("全部日期和时间信息:%tc%n",date);
//f的使用
System.out.printf("年-月-日格式:%tF%n",date);
//d的使用
System.out.printf("月/日/年格式:%tD%n",date);
//r的使用
System.out.printf("HH:MM:SS PM格式(12时制):%tr%n",date);
//t的使用
System.out.printf("HH:MM:SS格式(24时制):%tT%n",date);
//R的使用
System.out.printf("HH:MM格式(24时制):%tR",date);
}
}
全部日期和时间信息:星期一九月1010:43:36CST2012
年-月-日格式:2012-09-10
月/日/年格式:09/10/12
HH:MM:SSPM格式(12时制):10:43:36上午
HH:MM:SS格式(24时制):10:43:36
HH:MM格式(24时制):10:43
3、时间休眠(sleep)
- 使当前线程进入停滞状态(阻塞进程),让出CPU的使用,给其他线程留出机会
- 休眠的单位:毫秒(1000毫秒 = 1秒)
Thread.sleep(休眠时间)
importjava.util.*;
publicclassSleepDemo {
publicstaticvoidmain(String[] args) {
try {
System.out.println(newDate( ) +"\n");
Thread.sleep(1000*3); // 休眠3秒
System.out.println(newDate( ) +"\n");
} catch (Exceptione) {
System.out.println("Got an exception!");
}
}
}
4、Calendar类
- 在java.util包下
- Calendar是抽象类,由于语言敏感性,Calendar类在创建对象时并非直接创建,而是通过静态方法创建,将语言敏感内容处理好,再返回子类对象
- 设置和获取日期数据的特定部分
- 通过Calendar类的getInstance()方法创建
publicstaticvoidmain(Stringargs[]) {
Calendarc=Calendar.getInstance();//默认是当前日期
System.out.println(c);
}
//输出
java.util.GregorianCalendar[time=1556350818634,areFieldsSet=true,areAllFields
Set=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offs
et=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null],firs
tDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2019,MONTH=3,WEEK_OF_YEAR=17
,WEEK_OF_MONTH=4,DAY_OF_MONTH=27,DAY_OF_YEAR=117,DAY_OF_WEEK=7,DAY_OF_WEEK_IN
_MONTH=4,AM_PM=1,HOUR=3,HOUR_OF_DAY=15,MINUTE=40,SECOND=18,MILLISECOND=634,ZO
NE_OFFSET=28800000,DST_OFFSET=0]
- 创建一个指定日期的Calendar对象
注意:月份的起始值是0,[0,11]!!!
//创建一个代表2019年4月27日的Calendar对象
Calendarc1=Calendar.getInstance();
c1.set(2019, 4-1, 27);
- Calendar类对象字段类型
常量 | 描述 |
Calendar.YEAR | 年份 |
Calendar.MONTH | 月份 |
Calendar.DATE | 日期 |
Calendar.DAY_OF_MONTH | 日期,和上面的字段意义完全相同 |
Calendar.HOUR | 12小时制的小时 |
Calendar.HOUR_OF_DAY | 24小时制的小时 |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.DAY_OF_WEEK | 星期几 |
// 获得年份
intyear=c1.get(Calendar.YEAR);
// 获得月份
intmonth=c1.get(Calendar.MONTH) +1;
// 获得日期
intdate=c1.get(Calendar.DATE);
// 获得小时
inthour=c1.get(Calendar.HOUR_OF_DAY);
// 获得分钟
intminute=c1.get(Calendar.MINUTE);
// 获得秒
intsecond=c1.get(Calendar.SECOND);
// 获得星期几(注意(这个与Date类是不同的):1代表星期日、2代表星期1、3代表星期二,以此类
推)
intday=c1.get(Calendar.DAY_OF_WEEK);
// 设置完整字段
c1.set(2009, 6-1, 12);//把Calendar对象c1的年月日分别设这为:2009、6、12
// 设置某个字段
c1.set(Calendar.DATE,10);
c1.set(Calendar.YEAR,2008);
//其他字段属性set的意义以此类推
//把c1对象的日期加上10,也就是c1也就表示为10天后的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, 10);
//把c1对象的日期减去10,也就是c1也就表示为10天前的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, -10);
- 注意:日期设为0之后,就会变为上月的最后一天
publicstaticvoidmain(String[] args) {
Calendarc1=Calendar.getInstance();
c1.set(2017, 2, 1);
System.out.println(c1.get(Calendar.YEAR)
+"-"+c1.get(Calendar.MONTH)
+"-"+c1.get(Calendar.DATE));
c1.set(2017, 2, 0);
System.out.println(c1.get(Calendar.YEAR)
+"-"+c1.get(Calendar.MONTH)
+"-"+c1.get(Calendar.DATE));
}
//输出
2017-2-1
2017-1-28 2相当于3月,日期为0变为上月的最后一天,即2月28