内存存储区域
引用变量和普通变量引用变量放在栈中,基本数据类型的内容是在堆内存中。
对象放在堆内存中,其引用变量放在栈中,指向堆内存存放对象的地址。
静态变量放在静态区中,静态变量在程序的执行始终只分配一次,但可改变值,常可作计数器用。

常量放在常量池中

上面s1s2是false,它们不是同一个对象,只是内容相同而异。s1s3 true,内存中的常量池不允许有重复的值。
单例模式
什么是单例模式
单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。
单例模式和静态类的区别
首先理解一下什么是静态类,静态类就是一个类里面都是静态方法和静态field,构造器被private修饰,因此不能被实例化。Math类就是一个静态类。
Math类:
public static final double PI = 3.14159265358979323846;
知道了什么是静态类后,来说一下他们两者之间的区别:
1)首先单例模式会提供给你一个全局唯一的对象,静态类只是提供给你很多静态方法,这些方法不用创建对象,通过类就可以直接调用;
2)单例模式的灵活性更高,方法可以被override,因为静态类都是静态方法,所以不能被override;
3)如果是一个非常重的对象,单例模式可以懒加载,静态类就无法做到;
那么时候时候应该用静态类,什么时候应该用单例模式呢?首先如果你只是想使用一些工具方法,那么最好用静态类,静态类比单例类更快,因为静态的绑定是在编译期进行的。如果你要维护状态信息,或者访问资源时,应该选用单例模式。还可以这样说,当你需要面向对象的能力时(比如继承、多态)时,选用单例类,当你仅仅是提供一些方法时选用静态类。
如何实现单例模式
- 饿汉模式
所谓饿汉模式就是立即加载,一般情况下再调用getInstancef方法之前就已经产生了实例,也就是在类加载的时候已经产生了。这种模式的缺点很明显,就是占用资源,当单例类很大的时候,其实我们是想使用的时候再产生实例。因此这种方式适合占用资源少,在初始化的时候就会被用到的类。
class SingletonHungary {
private static SingletonHungary singletonHungary = new SingletonHungary();
//将构造器设置为private禁止通过new进行实例化
private SingletonHungary() {
}
public static SingletonHungary getInstance() {
return singletonHungary;
}
}
懒汉模式
懒汉模式就是延迟加载,也叫懒加载。在程序需要用到的时候再创建实例,这样保证了内存不会被浪费。针对懒汉模式,我们暂时给出了1种最简单实现方式,有线程安全的问题,我们在学习多线程后,在完善这里的写法:
class SingletonLazy1 {
private static SingletonLazy1 singletonLazy;
private SingletonLazy1() {
}
public static SingletonLazy1 getInstance() {
if (null == singletonLazy) {
singletonLazy = new SingletonLazy1();
}
return singletonLazy;
}
}
学到这里,你可能会对设计模式感到难以理解,单列模式其实就是一种最常用的设计模式。
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
现阶段的你,我的意见或者经验就是,不懂先记住,留待以后随着学习的知识越来越多,越来越深入后慢慢理解!
final,static关键字
final表面意思就是不可更改的,恒量的意思;指的是无法改变的量,这与静态标量static是有区别的,静态变量指的是只有一份存储空间,值是可以改变的。使用final一定原因是出于软件设计的角度,因为别人看到final这个关键字就知道是什么意思,达到心领神会的效果,但也正是由于这种"语义"的存在,在程序设计中要谨慎使用,以免误用。
在Java中final修饰的就是常量,而且变量名要大写;
Math类:
public static final double PI = 3.14159265358979323846;
......java源码中好多变量都用final修饰
final的作用
final根据修饰位置的不同作用也不相同,针对三种情况:
- 修饰变量,被final修饰的变量必须要初始化,赋初值后不能再重新赋值。注意:局部变量不在我们讨论的范畴,因为局部变量本身就有作用范围,不使用private、public等词修饰。虽然final可以修饰局部变量,但是一般情况下,我们在实际应用中几乎不会这么干!没多大的意义!
- 修饰方法,被final修饰的方法代表不能重写。
- 修饰类,被final修饰的类,不能够被继承。
注意:final修饰的类,类中的所有成员方法都被隐式地指定为final方法。
final修饰变量
被final修饰的变量必须显示的初始化,初始化可以以三种方式:1)定义时初始化,2)在构造器中设置值,3)在非静态块中为final实例变量设置值。
final修饰变量指的是:这个变量被初始化后便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可变,即不能再指向其他的对象。
public class FinalTest {
//直接赋值
final int x1= 10000;
final int x2;
final int x3;
//初始化块中
{
x2 = 20000;
}
//构造器中
public FinalTest() {
this.x3 = 3000;
}
}

final和static的区别
static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变,看一下例子:
public class Test {
public static void main(String[] args) {
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
System.out.println(myClass1.i);
System.out.println(myClass1.j);
System.out.println(myClass2.i);
System.out.println(myClass2.j);
//myClass1.i = 1233; //这里不可以改的
}
}
class MyClass {
public final double i = Math.random();
public static double j = Math.random();
}
--------------------------------------------------------------------
//由于i是两个对象的成员,所以值不一样,并不是说它可以改final变量
//j是程序一加载到JVM中,只初始分配一次,后面不管创建多少对象,都是共享这一块内存
0.2356062719054033
0.7981894049184048
0.7240468555989844
0.7981894049184048
基于final和static的特性,我们往往在实际的程序开发中,需要要给全局范围内的常数,一般定义为:如
public static final int CHANGSHU=100;
static知识
在《Java编程思想》P86页有这样一段话:
“static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。因为this是代指一相对象本身,所以this是一个对象”
这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键字的基本作用,简而言之,一句话来描述就是:
方便在没有创建对象的情况下来进行调用(方法/变量)。
很显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。
1)static方法
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。举个简单的例子:
static能作用于局部变量么?
在Java中切记:static是不允许用来修饰局部变量。不要问为什么,这是Java语法的规定。
static关键字的误区
static关键字会改变类中成员的访问权限吗?
Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问
jar打包的文件
JAR文件的全称是Java Archive File,意思就是Java档案文件。通常JAR文件是一种压缩文件,与常见的ZIP压缩文件兼容。JAR文件与zip文件的区别就是在JAR文件中默认包含了一个名为META-INF/MANIFEST.MF的清单文件,这个清单文件是在生成JAR文件时系统自动创建的。
当开发了一个应用程序后,这个应用程序包含了很多类,如果需要把这个应用程序提供给别人使用,通常会将这些类文件打包成一个JAR文件,把这个JAR文件提供给别人使用。只要别人在系统的CLASSPATH环境变量中添加这个JAR文件,就可以想在本地自己写的代码一样地使用Jar包里的代码。
使用JAR文件有以下好处:
安全。能够对JAR文件进行数字签名,只让能够识别数字签名的用户使用里面的东西。
加快下载速度。在网上使用applet时,如果存在多个文件而不打包,为了能够把每个文件都下载到客户端,需要为每个文件单独建立一个HTTP连接,这是非常耗时的工作。将这些文件压缩成一个JAR包,只要建立一个http连接就能够一次下载所有的文件。
压缩。使文件变小,JAR的压缩机制和zip完全相同
包封装。能够让JAR包里面的文件依赖于统一版本的类文件。
可一致性。JAR包作为内嵌在Java平台内部处理的标准,能够在各种平台上直接使用。
如何开发jar包

开发jar包的人员开发流程:
步骤1:创建一个项目(STS/ECLIPSE中)编写类
步骤2:打包成jar包
在要导出的类上,右键->Export->java -> JAR file,然后选择路径,如桌面上。
如何使用jar包
当别人发过来一个jar包后,比如上面的sum.jar包,如何使用呢?
使用方法:
步骤1:在另一工程目录下新建文件夹lib,将需要使用的jar包复制进去(ctrl c、ctrl v)。在工程上右键刷新就可以看到jar包了
步骤2:在需要使用的jar包上,右键->build path ->Add to build Path,或右击项目名—Properties—JavaBuildPath—Libiraries—Add Externals JARS…
步骤3:编写测试类JarXiongTest.java

打包可执行的jar包
1.java类中要有main方法,
2.导包—
3.
4.测试:打DOS----javac xxxx.java----java -jar xxx.jar
枚举
理解枚举类型
枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看如何写一个枚举?
枚举的定义语法
在没有枚举类型时定义常量常见的方式
public class DayDemo {
public static final int MONDAY =1;
public static final int TUESDAY=2;
public static final int WEDNESDAY=3;
public static final int THURSDAY=4;
public static final int FRIDAY=5;
public static final int SATURDAY=6;
public static final int SUNDAY=7;
}
上述的常量定义常量的方式称为int枚举模式,这样的定义方式并没有什么错,但它存在许多不足,如在类型安全和使用方便性上并没有多少好处,如果存在定义int值相同的变量,容易混淆,因此这种方式在枚举出现后并不提倡,现在我们利用枚举类型来重新定义上述的常量,定义周一到周日的常量
枚举类型,使用关键字enum
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
相当简洁,在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。
枚举类型Day中分别定义了从周一到周日的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天。
写好后该如何使用呢?如下:
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumDemo {
public static void main(String[] args){
//直接引用
Day day =Day.MONDAY;
}
}
就像上述代码那样,直接引用枚举的值即可,这便是枚举类型的最简单模型。
枚举实现原理
实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。我们可以看看反编译的结果!
我们可以下载一个反编译工具打开字节码文件,看看生成的类



结论: 从反编译的代码可以看出编译器确实帮助我们生成了一个Day类而且该类继承自java.lang.Enum类,该类是一个抽象类,除此之外,编译器还帮助我们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期。还为我们生成了两个静态方法,分别是values()和 valueOf(),到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好常量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;
编译器生成的Values方法与ValueOf方法
values()方法和valueOf(String name)方法是编译器生成的static方法,后面我们自己定义的枚举类的父类Enum的分析中,在Enum类中并没出现values()方法,但valueOf()方法还是有出现的,只不过编译器生成的valueOf()方法需传递一个name参数,而Enum自带的静态方法valueOf()则需要传递两个方法,从前面反编译后的代码可以看出,编译器生成的valueOf方法最终还是调用了Enum类的valueOf方法,下面通过代码来演示这两个方法的作用:
public class TestMain {
public static void main(String[] args) {
Day day1=Day.MONDAY;
System.out.println(day1);
System.out.println();
Day[] days = Day.values();
for(Day day:days) {
System.out.println(day); //相等于day.toString()
}
Day monday = Day.valueOf("MONDAY"); //返回的是一个对象,不是文字描述
System.out.println(monday); //打印的是文字描述
}
}
---------------------------------------------------------------------------------
MONDAY
MONDAY
TUESDAY
WENDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY
MONDAY
从结果可知道,values()方法的作用就是获取枚举类中的所有变量,并作为数组返回,而valueOf(String name)方法与Enum类中的valueOf方法的作用类似根据名称获取枚举变量,只不过编译器生成的valueOf方法更简洁些只需传递一个参数。
Enum抽象类常见方法
Enum是所有 Java 语言枚举类型的公共基本类(注意Enum是抽象类),以下是它的常见方法:

ordinal()方法,该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,如日期中的MONDAY在第一个位置,那么MONDAY的ordinal值就是0,如果MONDAY的声明位置发生变化,那么ordinal方法获取到的值也随之变化,注意在大多数情况下我们都不应该首先使用该方法,毕竟它总是变幻莫测的。
public class TestMain {
public static void main(String[] args) {
Day monday = Day.valueOf("MONDAY"); //返回的是一个对象,不是文字描述
System.out.println(monday); //打印的是文字描述
int index=monday.ordinal();
System.out.println(index);
}
}
----------------------------------------------------------------------
MONDAY
0
compareTo(E o) 方法则是比较枚举的大小,注意其内部实现是根据每个枚举的ordinal值大小进行比较的。大返回1,小返回-1.
Day monday = Day.valueOf("MONDAY"); //返回的是一个对象,不是文字描述
Day tuesday = Day.valueOf("TUESDAY");
System.out.println(tuesday.compareTo(monday));
-------------------------------------------
1
name()方法 与toString()几乎是等同的,都是输出变量的字符串形式。
valueOf(Class enumType, String name)方法 则是根据枚举类的Class对象和枚举名称获取枚举常量(对象也可为常量),注意该方法是静态的。
Day tuesday1 = Day.valueOf(Day.class, "TUESDAY");
System.out.println(tuesday1.toString());
枚举的进阶
向enum类添加方法与自定义属性和构造函数
上面通过反编译我们可以看到,每个常量都是对象,不是基本数据类型数据,它在static块中,初始化了所有的常量。
重新定义一个日期枚举类,带有desc成员变量描述该日期的对于中文描述,同时定义一个getDesc方法,返回中文描述内容,自定义私有构造函数,在声明枚举实例时传入对应的中文描述,代码如下:
public enum Day2 {
MONDAY("星期一",1),
TUESDAY("星期二",2),
WEDNESDAY("星期三",3),
THURSDAY("星期四",4),
FRIDAY("星期五",5),
SATURDAY("星期六",6),
SUNDAY("星期日",7); //记住要用分号结束
private String desc; //枚举对象的文字描述
private int code; //枚举对应的位置
private Day2(String desc,int code) {//私有构造器,外部没法使用它
this.desc = desc;
this.code= code;
}
/**
* 定义方法,返回描述,跟常规类的定义没区别
* @return
*/
public String getDesc(){
return desc;
}
/**
* 定义方法,返回代码,跟常规类的定义没区别
* @return
*/
public int getCode(){
return code;
}
//通过自定义的code属性,来获取整个枚举对象,
public static Day2 getDayByCode(int code) {
Day2 day=null;
switch (code) {
case 1:
day= Day2.MONDAY;
break;
case 2:
day= Day2.TUESDAY;
break;
case 3:
day= Day2.WEDNESDAY;
break;
case 4:
day= Day2.THURSDAY;
break;
case 5:
day= Day2.FRIDAY;
break;
case 6:
day= Day2.THURSDAY;
break;
case 7:
day= Day2.SUNDAY;
break;
}
return day;
}
}
Object类
Object类是一个特殊的类,是所有类的父类,如果定义一个类没有用extends明确指出继承于某个类,那么它默认继承Object类。
类Object是类层次结构的根类
所有对象,包括数组在内,都实现了这个类中的方法
Object类没有属性,只有方法,而且我们可以从源码中看到大多数方法都是native方法:
对native关键字做一个总结:
native关键字是Java与C++/C联合开发的时候用的,java自己开发不用!
使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了dll,由java去调用。这个函数的实现是在DLL中,JDK的源代码中并不包含,所以看不到被这个关键字修饰的函数的源代码。对于不同的平台它们是不同的。这也是java的底层机制,实际上java就是在不同平台上调用不同的native方法实现对操作系统的访问。
简言之,native是用做java和其他语言(如C)进行协作时用的,也就是native后的函数的实现不是用java写的。
native的意思就是通知操作系统,这个函数你必须给我实现,因为我要用。所以native关键字的函数就是操作系统实现的,java只能调用。
java是跨平台的语言,既然是跨平台,所付出的代价就是牺牲一些对底层的控制,而java要实现对底层的控制,就需要一些其他语言的帮助,这个就是native的作用了
Object的主要常用方法
1.public final native Class<?> getClass()方法
该方法也是native方法,返回的是该Object对象(或后代类)的类对象/运行时类对象。
类对象:在java中,类是对具有一组相同特征或者行为的实例的抽象并进行描述,对象是该类所描述的特征或者行为的具体实例。作为概念层次的类,其本身也具有某些共同的属性,如都具有类型、类加载器、包、父类、属性、方法等。java中有专门定义一个类,Class(C是大写),该类用于描述其他类所具有的这些特征。因此,从该角度来看,类本身也都是属于Class类的对象。 该部分涉及到反射的知识。
2. public native int hashCode()方法
hashCode返回的值:一般是通过将该对象的内存地址转换成一个整数来实现。该方法不是java实现的,因此使用了native关键字.
public class ObjectTest {
public static void main(String[] args) {
ObjectTest demo = new ObjectTest();
System.out.println(demo.hashCode());
ObjectTest demo2 = new ObjectTest();
System.out.println(demo2.hashCode());
}
}
--------------------------------------------------------------------------
2018699554
1311053135
2.public boolean equals(Object obj)方法
有关equals()方法与==运算符的区别,前面的课程中有过讲解,但是在这里,还要进一步的讨论一个问题:重写equals方法时一定要重写hashCode方法,为什么?
我们知道在Object类中,hashCode方法是通过Object对象的地址计算出来的,因为Object对象只与自身相等,所以同一个对象的地址总是相等的,计算取得的哈希码也必然相等,对于不同的对象,由于地址不同,所获取的哈希码自然也不会相等。因此到这里我们就明白了,如果一个类重写了equals方法,但没有重写hashCode方法,将会直接违反重写equals方法必须要有对称性原则:对于任何非null的引用值,当x.equals(y)返回true时,y.equals(x)一定返回true的规定,这样的话,,就无法达到我们预期想要的效果。如碰到map集合就不那完美了。
例:下面代码中,定义了两个类,Key,作为键象,Vlaue,作为值对象
public class ObjectTest {
public static void main(String[] args) {
String s1 = new String("key");
String s2= new String("key");
//定义一个Map集合,有两种对象,键值对形式的集合
Map<String,Value> map1 = new HashMap<>();
Value value=new Value(12);
map1.put(s1,value);
System.out.println(s1.equals(s2)); //true,因为字符串类,内部定义了equals方法,只要值相等就相等
//下面两行输出的结果是一样的,因为String类中重写了equals,也重写hasCode方法
System.out.println(map1.get(s1)); //12
System.out.println(map1.get(s2)); //12
Map<Key,Value> map2 = new HashMap<>();
Value value2 = new Value(32);
Key key1=new Key("11");
Key key2 = new Key("11");
System.out.println(key1.equals(key2)); //true
System.out.println(map2.get(key1)); //32
System.out.println(map2.get(key2)); //null,拿不出值来了,这里两个Key不是相等的了,我们只是重写的法中比较值相等
}
//静态内部类Key,Value,它不依附于外围类创建对象实例,当然也可另外在同包或其它包中创建独立的两个类,但不能是静态类,没有这种说法
static class Key {
private String k;
public Key(String k) {
this.k=k;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Key) {
Key oKey=(Key)obj;
return oKey.k.equals(this.k);
}
return false;
}
}
static class Value{
private int v;
public Value(int v) {
this.v=v;
}
@Override
public String toString() {
return "Value [v=" + v + "]";
}
}
}
---------------------------------------------------------------------------------------------
true
Value [v=12]
Value [v=12]
true
Value [v=32]
null
对于s1和s2的结果,我们并不惊讶,因为相同的内容的s1和s2获取相同内的value这个很正常,因为String类重写了equals方法和hashCode方法,使其比较的是内容和获取的是内容的哈希码。
但是对于k1和k2的结果就不太尽人意了,k1获取到的值是2,k2获取到的是null,这是为什么呢?想必大家已经发现了,Key只重写了equals方法并没有重写hashCode方法,这样的话,equals比较的确实是内容,而hashCode方法呢?没重写,那就肯定调用超类Object的hashCode方法,这样返回的不就是地址了吗?k1与k2属于两个不同的对象,返回的地址肯定不一样,所以现在我们知道调用map2.get(k2)为什么返回null了吧!
解决也很简单,重写hashCode方法,返回equals方法判断相等的关键属性的hashCode值:
@Override
public int hashCode() {
return k.hashCode(); //hashCode是父类中定义好了
}
系统中的hashCode(),equals(Object obj)


重写了hashCode()方法后的代码如下
public class ObjectTest {
public static void main(String[] args) {
//定义一个Map集合,有两种对象,键值对形式的集合
Map<Key,Value> map2 = new HashMap<>();
Value value2 = new Value(32);
Key key1=new Key("11");
Key key2 = new Key("11");
map2.put(key1, value2);
System.out.println(key1.equals(key2)); //true
System.out.println(map2.get(key1)); //32
System.out.println(map2.get(key2)); //32
}
//静态内部类Key,Value,
static class Key {
private String k;
public Key(String k) {
this.k=k;
}
//重写hashCode()方法
@Override
public int hashCode() {
return k.hashCode(); //hashCode是父类中定义好了
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Key) {
Key oKey=(Key)obj;
return oKey.k.equals(this.k);
}
return false;
}
}
static class Value{
private int v;
public Value(int v) {
this.v=v;
}
@Override
public String toString() {
return "Value [v=" + v + "]";
}
}
}
---------------------------------------------------------------------------
true
Value [v=32]
Value [v=32]
3.protected native Object clone( )
clone()方法又是一个被声明为native的方法,因此,我们知道了clone()方法并不是Java的原生方法,具体的实现是有C/C++完成
clone英文翻译为”克隆”,其目的是创建并返回此对象的一个副本。
Java术语表述为:clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。
public class CloneDemo implements Cloneable{
public static void main(String[] args) throws CloneNotSupportedException {
Object obj=new Object();
//obj.clone(); //不可调用clone方法,因为这是父类的对象,要子类的对象才可调用父类的protected方法
CloneDemo cloneDemo=new CloneDemo();
Object obj2=(Object)cloneDemo.clone();
System.out.println(obj);
System.out.println(obj2);
}
}
注意问题“不同包中的子类可以访问”,是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员(属性/方法)。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员!
clone()的正确调用是需要实现Cloneable接口,如果没有实现Cloneable接口,并且子类直接调用Object类的clone()方法,则会抛出CloneNotSupportedException异常。
Cloneable接口仅是一个标记接口,接口本身不包含任何方法,用来指示Object.clone()可以合法的被子类引用所调用。
- Obejct类的clone()方法实现的是浅拷贝
- 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员。调用者为子类的引用时才能访问父类中用protected修饰的成员
- 想要在子类中调用父类的clone()方法,子类需要实现Cloneable接口,该接口用来指示Object.clone()可以合法的被子类引用的标记
- 简单谈谈什么时候需要用到clone方法呢,开发飞机大战的游戏,发射出来的子弹,每个子弹就是一个对象,这个时候就可以用clone方法复制子弹对象,它是一种浅拷贝,可以大量节约内存的开销。
- clone()与new的区别:
(1)在java中clone()与new都能创建对象。
(2)clone()不会调用构造方法;new会调用构造方法。
(3)clone()能快速创建一个已有对象的副本,即创建对象并且将已有对象中所有属性值克隆;new只能在JVM中申请一个空的内存区域,对象的属性值要通过构造方法赋值。
注意:
(1)使用clone()类必须实现java.lang.Cloneable接口并重写Object类的clone()方法,如果没有实现Cloneable()接口将会抛出CloneNotSupportedException异常。(此类实现java.lang.Cloneable接口,指示Object.clone()方法可以合法的对该类实例进行按字段复制。)
(2)默认的Object.clone()方法是浅拷贝,创建好对象的副本然后通过“赋值”拷贝内容,如果类包含引用类型变量,那么原始对象和克隆对象的引用将指向相同的引用内容。
面试题:什么是浅拷贝?什么是深拷贝?
“浅拷贝”:默认的Object.clone()方法,对于引用类型成员变量拷贝只是拷贝“值”即地址,没有在堆中开辟新的内存空间。
“深拷贝”:重写clone()方法,拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝,会在堆中开辟新的内存空间。
4.public String toString()方法
当使用System.out.println(Object obj);时,返回的就是该obj对象的toString方法,实际上System.out.println()内部是通过toString实现的

getClass()返回对象的类对象,getClassName()以String形式返回类对象的名称(含包名)。Integer.toHexString(hashCode())则是以对象的哈希码为实参,以16进制无符号整数形式返回此哈希码的字符串表示形式。
如u1对象的哈希码是638,则对应的16进制为27e,调用toString()方法返回的结果为:com.corn.User@27e。包名.类名@哈希码
因此:toString()是由对象的类型和其哈希码唯一确定,同一类型但不相等的两个对象分别调用toString()方法返回的结果不可能相同,除非重写了toString()方法。
Objects类,是一个工具类
java7里面新增的Objects类,该类的主要用途是将操作对象的一切常用操作进行的封装。包括hashCode、equals等。
java8在7的基础上又新增一些方法。
知识点一:Objects类简介
本类由一些操作对象的静态工具方法构成,这些工具方法包括了非空检查、方法的非空参数检查、比较对象的hashCode、为对象返回一个字符串、比较两个对象。
知识点二:Objects特点
- 该类是使用final修饰,不能被继承
- 该类的构造方法被私有化(使用private修饰),不能直接创建对象
- 该类中所有的方法都是静态方法,可以使用类型直接调用(对应2,该类不需要创建对象)
知识点三:Objects内方法介绍
//比较两个对象是否相等(首先比较内存地址,然后比较a.equals(b),只要符合其中之一返回true)
public static boolean equals(Object a, Object b);
//深度比较两个对象是否相等(首先比较内存地址,相同返回true;如果传入的是数组,则比较数组内的对应下标的值是否相同)
public static boolean deepEquals(Object a, Object b);
//返回对象的hashCode*,若传入的为null,返回0
public static int hashCode(Object o);
//返回传入可变参数的所有值的hashCode的(加工后)总和(这里说总和有点牵强,具体参考
Arrays.hashCode()方法)
public static int hash(Object... values);
System.out.println("a".hashCode());
System.out.println(Objects.hashCode("a"));
System.out.println(Objects.hash("a"));
public class ObjectsToolTest {
public static void main(String[] args) {
Integer[] a1= new Integer[] {1,2,3,4};
Integer[] a2=new Integer[] {1,3,3,5};
System.out.println(Objects.deepEquals(a1, a2));
String s11="1";
String s1="a";
String s2="b";
String s3="c";
String s="abc";
String string=null;
System.out.println(Objects.hashCode(s11));
System.out.println(Objects.hashCode(s1));
System.out.println(Objects.hashCode(s2));
System.out.println(Objects.hashCode(s3));
System.out.println(Objects.hashCode(s));
System.out.println(Objects.hashCode(string));
}
}
-------------------------------------------------------------------------------------------------------------------
false
49
97
98
99
96354
0
//返回对象的String表示,若传入null,返回null字符串,这个可以用来处理空指针异常的一种方法。
public static String toString(Object o)
public class ObjectsToolTest {
public static void main(String[] args) {
//Objects.toString(Object o)与Object.toString()功能一样,但Object.toString()可能报null异常
String string=null;
String s1=string.toString();
String s2 = Objects.toString(string);
System.out.println(s1);
}
}
----------------------------------------------------------------------------------------------------------------------------------
Exception in thread "main" java.lang.NullPointerException
at cn.ybzy.javabasic.object.ObjectsToolTest.main(ObjectsToolTest.java:26)
public class ObjectsToolTest {
public static void main(String[] args) {
//Objects.toString(Object o)与Object.toString()功能一样,但Object.toString()可能报null异常
String string=null;
//String s1=string.toString();
String s2 = Objects.toString(string);
System.out.println(s2);
}
}
----------------------------------------------------------------------------------------------
null
//返回对象的String表示,若传入null,返回默认值nullDefault
public static String toString(Object o, String nullDefault)
//使用指定的比较器c 比较参数a和参数b的大小(相等返回0,a大于b返回整数,a小于b返回负数)
public static <T> int compare(T a, T b, Comparator<? super T> c)
//如果传入的obj为null抛出NullPointerException,否者返回obj
public static <T> T requireNonNull(T obj)
//如果传入的obj为null抛出NullPointerException并可以指定错误信息message,否者返回obj
public static <T> T requireNonNull(T obj, String message)
//判断传入的obj是否为null,是返回true,否者返回false
public static boolean isNull(Object obj)
//判断传入的obj是否不为null,不为空返回true,为空返回false (和isNull()方法相反)
public static boolean nonNull(Object obj)
//如果传入的obj为null抛出NullPointerException并且使用参数messageSupplier指定错误信息,否者返回obj
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)
知识点五:总结
Objects类给我们提供了一些常用的操作对象的方法,我们可以直接使用,这是非常方便的。尤其是requireNonNull()方法,
在我们写方法时需要判断传入的参数是否为null可以使用该方法及其方便。
日期时间类

关于日期时间类Date和Calender,存在很多问题:
- Date的缺陷,我们知道 Date的setYear和getYear等函数是删除线显示的
原因在:比如2009-01-04日,那么获取的年竟然是109,所以是有问题的 - Calender常常用于时间的回卷,经常使用的就是roll(Day_of_Year,-7)就是七天前
但是如果是2009-01-04日,那么七天前是2009-12-28日,而非2008年,这是因为它只对天回卷了,年没有回卷 - 显示转换和时区问题,都麻烦,不好处理
所以Date类中有很多方法都标有删除线,表示过时了,但是我们还是要对这样的方法稍稍了解一下,然后看看新的日期时间类!
一、 Date类
Date类对象的创建:
1、创建一个当前时间的Date对象
//创建一个代表系统当前日期的Date对象
Date d = new Date();
2、创建一个我们指定的时间的Date对象:
使用带参数的构造方法Date(int year, int month, int day) ,可以构造指定日期的Date类对象,Date类中年份的参数应该是实际需要代表的年份减去1900,实际需要代表的月份减去1以后的值。
//创建一个代表2014年6月12号的Date对象
Date d1 = new Date(2014-1900, 6-1, 12); (注意参数的设置)
例:正确获得一个date对象所包含的信息
如:
Date d2 = new Date(2014-1900, 6-1, 12);
//获得年份 (注意年份要加上1900,这样才是日期对象d2所代表的年份)
int year = d2.getYear() + 1900;
//获得月份 (注意月份要加1,这样才是日期对象d2所代表的月份)
int month = d2.getMonth() + 1;
//获得日期
int date = d2.getDate();
//获得小时
int hour = d2.getHours();//不设置默认为0
//获得分钟
int minute = d2.getMinutes();
//获得秒
int second = d2.getSeconds();
//获得星期 (注意:0代表星期日、1代表星期1、2代表星期2,其他的一次类推了)
int day = d2.getDay();

3、创建一个我们指定的时间的Date对象:
Date date3 =new Date(1255445555L);
System.out.println(date3); //从1970年1月1日0:00:00开始过云的毫秒数
-----------------------------------------------------------------------
Thu Jan 15 20:44:05 CST 1970
二、Calendar类
Calendar类的功能要比Date类强大很多,可以方便的进行日期的计算,获取日期中的信息时考虑了时区等问题。而且在实现方式上也比Date类要复杂一些
Calendar类对象的创建
Calendar类是一个抽象类,由于Calendar类是抽象类,且Calendar类的构造方法是protected的,所以无法使用Calendar类的构造方法来创建对象,API中提供了getInstance方法用来创建对象。
1、创建一个代表系统当前日期的Calendar对象
Calendar c = Calendar.getInstance();//默认是当前日期
2、创建一个指定日期的Calendar对象
使用Calendar类代表特定的时间,需要首先创建一个Calendar的对象,然后再设定该对象中的年月日参数来完成。
//创建一个代表2014年5月9日的Calendar对象
Calendar c1 = Calendar.getInstance();
c1.set(2014, 5 - 1, 9);//调用:public final void set(int year,int month,int date)
Calendar类对象信息的设置与获得
1)Calendar类对象信息的设置
A、Set设置
如:Calendar c1 = Calendar.getInstance();
调用:public final void set(int year,int month,int date)
c1.set(2014, 6- 1, 9);//把Calendar对象c1的年月日分别设这为:2014、6、9
B、利用字段类型设置
如果只设定某个字段,例如日期的值,则可以使用public void set(int field,int value)
//把 c1对象代表的日期设置为10号,其它所有的数值会被重新计算
c1.set(Calendar.DATE,10);
//把c1对象代表的年份设置为2015年,其他的所有数值会被重新计算
c1.set(Calendar.YEAR,2015);
其他字段属性set的意义以此类推*
Calendar类中用一下这些常量表示不同的意义,jdk内的很多类其实都是采用的这种思想
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——星期几
C、Add设置(可用与计算时间)
Calendar c1 = Calendar.getInstance();
//把c1对象的日期加上10,也就是c1所表的日期的10天后的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, 10);
//把c1对象的日期加上-10,也就是c1所表的日期的10天前的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, -10);
其他字段属性的add的意义以此类推
2)、Calendar类对象信息的获得(使用get())
public class CalendarTest {
public static void main(String[] args) {
Calendar calendar=Calendar.getInstance();
System.out.println(calendar);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH)+1; //月份加1
int day = calendar.get(Calendar.DATE);
int hour = calendar.get(Calendar.HOUR_OF_DAY); //一天的小时
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
// 获得星期几(注意(这个与Date类是不同的):1代表星期日、2代表星期1、3代表星期二,以此类推)
int weekday = calendar.get(Calendar.DAY_OF_WEEK)-1;
System.out.println(year+"年"+month+"月"+day+"日"+" "+"星期"+weekday+" "+hour+"时"+" "+minute+"分"+" "+second+"秒");
}
}
----------------------------------------------------------------------------
2025年3月17日 星期1 10时 6分 15秒
三、 GregorianCalendar类
GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家使用的标准日历系统。
1、GregorianCalendar类对象的创建
GregorianCalendar有自己的构造方法,而其父类Calendar没有公开的构造方法。
GregorianCalendar() 在具有默认语言环境的默认时区内使用当前时间构造一个默认的 GregorianCalendar。
在具有默认语言环境的默认时区内构造一个带有给定日期设置的
GregorianCalendar。
GregorianCalendar(int year, int month, int dayOfMonth)
GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute) 。
GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second)
2、创建一个代表当前日期的GregorianCalendar对象*
GregorianCalendar gc = new GregorianCalendar();
//创建一个代表2014年6月19日的GregorianCalendar对象(注意参数设置,与其父类是一样,月份要减去1)
GregorianCalendar gc = new GregorianCalendar(2014,6-1,19);
3、GregorianCalendar有一个方法:判断是否为闫年的这个方法稍微有点用了。
//boolean isLeapYear(int year); //确定给定的年份是否为闰年
GregorianCalendar gcd= new GregorianCalendar();
System.out.println("2025年是否为闰年:"+gcd.isLeapYear(2025));
四、DateFormat类和SimpleDateFormat类
public class SimpleDateFormat extends DateFormat
是一个以与语言环境相关的方式来格式化和分析日期的具体类。DateFormat是一个抽象类,而SimpleDateFormat实现了它,所以我们常用SimpleDateFormat类,而不用另外去实现它。当然了,可以这样做,不怕累的话。
(日期时间输出的工具类)
它允许进行日期格式化(日期 -> 文本)、分析(文本 -> 日期)和规范化。所以SimpleDateFormat类可以实现:String 到 Date,Date到String的互转
日期类对象之间的互转

1.Date类对象与long型时间的互转
//1.将Date类的对象转换为long型时间
Date d= new Date();
//使用对象的getTime()方法完成
long dLong = d.getTime();
//2.将long型时间转换为Date类的对象
long time = 1290876532190L;
//使用Date的构造方法完成
Date d2 = new Date(time);
2.Calendar对象和long型时间之间的互转
// 将Calendar对象转换为相对时间
Calendar c = Calendar.getInstance();
long t1 = c.getTimeInMillis();
// 将long转换为Calendar对象
Calendar c1 = Calendar.getInstance();
long t = 1252785271098L;
c1.setTimeInMillis(t1);
3.Calendar对象和Date对象之间的互转
//1 .将Calendar对象转换为Date(c.getTime())
Calendar c = Calendar.getInstance();
Date d = c.getTime();
// 2.将Date转换为Calendar对象(s.setTime(date))
Calendar c1 = Calendar.getInstance();
Date d1 = new Date();
//通过setTime()方法后,日历c1所表示的日期就d1的日期
c1.setTime(d1);
4.Date,String相互转化
//1.Date 转化String
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd");
String dateStr=sdf.format(new Date());
//2.String 转化Date
String str="2010-5-27";
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd");
Date birthday = sdf.parse(str);
public class SimpleDateFormatTest {
public static void main(String[] args) throws ParseException {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date));
String d="2025-03-17 10:36:51";
System.out.println(sdf.parse(d));
String curdate=sdf.format(System.currentTimeMillis());
System.out.println(curdate);
}
}
----------------------------------------------------------------------------------
2025-03-17 10:37:51
Mon Mar 17 10:37:51 CST 2025
2025-03-17 10:37:51
自已开发的常用时间工具类
l、判断是否是闰年
/**
* 给定一个年份判断该年份是否为闰年
*/
public static boolean isLeapYear(int year) {
GregorianCalendar calendar = new GregorianCalendar();
return calendar.isLeapYear(year);
}
2、获取当前日期的字符串表示形式
/**
* 利用SimpleDateFormat获取当前日期的字符串表示形式 格式:2009-5-05
*/
public static String getCurrentDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.format(System.currentTimeMillis());
}
3、给出任意一个年月日得到该天是星期几
/**
* 给出任意一个年月日得到该天是星期几
*/
public static int getWeek(String date) {
// 注意参数的大小写格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar c = Calendar.getInstance();
try {
Date d = dateFormat.parse(date);
c.setTime(d);
} catch (ParseException e) {
e.printStackTrace();
}
return c.get(Calendar.DAY_OF_WEEK)-1;
}
//应用
String curdate="2025-03-17";
System.out.println("今天是星期"+DateTools.getWeek(curdate));
4、获得距离今天n天的那一天的日期
/**
* 获得距离今天n天的那一天的日期
*/
public static String getDistanceDay(int day) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, day);
Date date = calendar.getTime();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.format(date);
}
/**
* 获得给定两个日期相差的天数
*/
public static long getGapDays(String date1,String date2) {
String[] d1=date1.split("-");
String[] d2=date2.split("-");
Calendar c=Calendar.getInstance();
//给Calendar对象设置指定的年月日时分秒
c.set(Integer.parseInt(d1[0]),Integer.parseInt(d1[1]),Integer.parseInt(d1[2]),0,0,0);
long l1=c.getTimeInMillis();
c.set(Integer.parseInt(d2[0]),Integer.parseInt(d2[1]),Integer.parseInt(d2[2]),0,0,0);
long l2=c.getTimeInMillis();
return (Math.abs(l1-l2)/(24*60*60*1000));
}
---- -----------------------------------------------------------------------------------------------
//利用工具类DataTools,获取两日期中间的天数
String datepast = "2010-12-12";
String datecur="2025-03-17";
System.out.println("2010-12-12到2025-03-17之间有:"+DateTools.getGapDays(datepast, datecur)+"天");
2010-12-12到2025-03-17之间有:5209天
java8后引入了一套全新的时间日期API
Greenwich Mean Time-GMT
格林尼治子午线上的平太阳时称为世界时(UT0),又叫格林尼治平时(GMT)
UTC - 协调世界时
Universal Time Coordinated
UTC是国际无线电咨询委员会制定和推荐的,UTC相当于本初子午线(即经度0度)上的平均太阳时。
它是经过平均太阳时(以格林威治标准时间GMT)、地轴运动修正后的新时标以及以「秒」为单位的国际原子时所综合精算而成的时间,计算过程相当严谨精密,因此若以「世界标准时间」的角度来说,UTC比GMT来得更加精准。
其误差值必须保持在0.9秒以内,若大于0.9秒则由位于巴黎的国际地球自转事务中央局发布闰秒,使UTC与地球自转周期一致。
所以基本上UTC的本质强调的是比GMT更为精确的世界时间标准.它其实是个更精确的GMT.
我们一般认为GMT和UTC是一样的,都与英国伦敦的本地时相同。
CST
CST(Central Standard Time ):却同时可以代表如下 4 个不同的时区:
Central Standard Time (USA) UT-6:00
Central Standard Time (Australia) UT+9:30
China Standard Time UT+8:00
Cuba Standard Time UT-4:00
CST可以同时表示美国,澳大利亚,中国,古巴四个国家的标准时间。
java.time包中的是类是不可变且线程安全的。新的时间及日期API位于java.time中,下面是一些关键类
●Instant——它代表的是时间戳
●LocalDate——不包含具体时间的日期,比如2014-01-14。它可以用来存储生日,周年纪念日,入职日期等。
●LocalTime——它代表的是不含日期的时间
●LocalDateTime——它包含了日期及时间,不过还是没有偏移信息或者说时区。
●ZonedDateTime——这是一个包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的。
1.java8是如何处理时间及日期的
java8中有个叫LocalDate的类,能用来表示今天的日期。这个类与java.util.Date略有不同,因为它只包含日期,没有时间。

public class Java8DateTest {
public static void main(String[] args) {
//java8新的日期时间类,获取当前时间
LocalDate localDate=LocalDate.now();
System.out.println(localDate);
LocalTime time1=LocalTime.now();
System.out.println(time1);
}
}
----------------------------------------------
2025-03-17
12:02:53.960
可以看到,他创建了今天的日期却不包含时间信息,并且格式化了日期。
2、如何在java8中获取当前的年月日
LocalDate类中提供了一些很方便的方法可以用来提取年月日以及其他的日期属性,特别方便,只需要使用对应的getter方法就可以了,非常直观

3、在java8中如何获取某个指定的日期对象
通过另一个方法,可以创建出任意一个日期,它接受年月日的参数,然后返回一个等价的LocalDate实例。在这个方法里,需要的日期你填写什么就是什么,不想之前的API中月份必须从0开始

4、在java8中检查两个日期是否相等
LocalDate重写了equals方法来进行日期的比较,如下所示:

5、在java8中如何检查重复事件对应的特殊日期,比如生日
在java中还有一个与时间日期相关的任务就是检查重复事件,比如每月的账单日
如何在java中判断是否是某个节日或者重复事件,使用MonthDay类。这个类由月日组合,不包含年信息,可以用来代表每年重复出现的一些日期或其他组合。他和新的日期库中的其他类一样也都是不可变且线程安全的,并且它还是一个值类(value class)。
public class Java8DateTest {
public static void main(String[] args) {
//java8新的日期时间类,获取当前时间
LocalDate localNow=LocalDate.now();
LocalDate datebirth =LocalDate.of(1989, 8, 13);
MonthDay birthday=MonthDay.from(datebirth);
MonthDay datenow=MonthDay.from(localNow);
//判断生两个日 期是否相等可用equals方法,该类重写了它
if(birthday.equals(datenow)) {
System.out.println("今天是你的生日!");
}else {
System.out.println("今天不是你的生日!");
}
}
}
6、如何在java8中获取当前时间
这个与第一个例子获取当前日期非常相似,这里用的是LocalTime类,默认的格式是hh:mm:ss:nnn

可以看到,这个时间是不包含日期的
7、如何增加时间里面的小时数,获取1周后的日期,一年前的日期
很多时候需要对时间进行操作,比如加一个小时来计算之后的时间,java8提供了更方便的方法 如plusHours,这些方法返回的是一个新的LocalTime实例的引用,因为LocalTime是不可变的
//如何增加时间里面的小时数,日期的天数.当前时间10小时后是几时,10天后是什么日期
LocalTime time1=timenow.plusHours(10);
LocalDate dweek=localNow.plusWeeks(1);
System.out.println("10小时后是:"+time1+"一周后是"+dweek);
//通用的方法plus,minus
LocalTime time2 = timenow.plus(12,ChronoUnit.HOURS);
LocalDate dweek2 = localNow.plus(2,ChronoUnit.WEEKS);
System.out.println("12小时后是"+time2+"两周后是"+dweek2);
System.out.println("一年前是"+localNow.minus(1,ChronoUnit.YEARS));
8、在java8中使用时钟类Clock,我们主要用来获取时间戳,时区
java8自带了Clock类,可以用来获取某个时区下(所以对时区是敏感的)当前的瞬时时间、日期。用来代替System.currentTimelnMillis()与TimeZone.getDefault()方法
System.out.println("Clock获取时间戳"+Clock.systemUTC().millis());
System.out.println("Clock获取系统默认的时区"+Clock.systemDefaultZone());
9、在java8中如何判断某个日期在另一个日期的前面还是后面
如何判断某个日期在另一个日期的前面还是后面或者相等,在java8中,LocalDate类中使用isBefore()、isAfter()、equals()方法来比较两个日期。如果调用方法的那个日期比给定的日期要早的话,isBefore()方法会返回true。equals()方法在前面的例子中已经说明了,这里就不举例了

LocalDateTime类

10、在java8中处理不同的时区
ZonId代表的是某个特定时区,ZonedDateTime代表带时区的时间,等同于以前的GregorianCalendar类。使用该类,可以将本地时间转换成另一个时区中的对应时间。
{
//时区处理
//在亚洲上海的这个时区里,怎么获取UTC时间,我们比UTC(英国时区早8个小时)
ZoneId zone = ZoneId.of("UTC");
ZonedDateTime zdt=ZonedDateTime.now(zone);
Instant instant=Instant.now();
ZonedDateTime zdt1=ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
System.out.println("UTC时间晚上海8个小时 "+zdt+" ==相等于== "+zdt1);
//在UTC世界标准时间上,获取其它地区的时间
LocalDateTime localTime =zdt.toLocalDateTime();
System.out.println("一个UTC时间:"+localTime);
ZonedDateTime chinaTime = ZonedDateTime.of(localTime,ZoneId.of("Asia/Shanghai"));
System.out.println(chinaTime);
}
--------------------------------------------------------------------------
UTC时间晚上海8个小时 2025-03-18T01:48:37.333Z[UTC] ==相等于== 2025-03-18T01:48:37.333Z[UTC]
一个UTC时间:2025-03-18T01:48:37.333
2025-03-18T01:48:37.333+08:00[Asia/Shanghai]
11、YearMonth,如何在java8中检查闰年
正如MonthDay表示的是某个重复出现的日子,YearMonth是另外一个组合,你可以用这个类找出这个月有多少天,LengthOfMonth()这个方法返回的是这个YearMonth实例对应的月有多少天,这对于检查2月是否润2月很有用
YearMonth yearMonth=YearMonth.of(2025, 2);
System.out.println("2025年2月有:"+yearMonth.lengthOfMonth()+"天");
System.out.println("2025年有:"+yearMonth.lengthOfYear()+"天");
LocalDate date = LocalDate.of(2025, 1, 1);
System.out.println("2025年是否为闰年:"+date.isLeapYear());
12、两个日期之间包含多少天,多少月,多少年(必须连起来)
计算两个日期之间对应相差多少天、月、年。可以用java.time.Period类完成该功能。
13、在java8中获取当前时间戳
java8获取时间戳特别简单。Instant类由一个静态的工厂方法now()可以返回当前时间戳
Clock类,获取UTC的时间戳
//Instant 时间戳类,与Date类对象互查转换,Date对象,会根据当前默认时区,补时间差,产生本地确的时间
Instant instant2 = Instant.now();
System.out.println(instant2);
Date date2 = Date.from(instant);
System.out.println(date2);
Instant instant3 = date2.toInstant();
System.out.println(instant3);
Long inst=Clock.systemUTC().millis();
Date date3 =new Date(inst);
System.out.println(date3);
-------------------------------------------------------------------
2025-03-18T02:15:36.298Z
Tue Mar 18 10:15:36 CST 2025
2025-03-18T02:15:36.297Z
Tue Mar 18 10:15:36 CST 2025
事实上Instant就是java8以前的Date,可以使用这个两个类中的方法在这两个类型之间进行转换,比如Date.from(Instant)就是用来把Instant转换成java.util.date的,而Date.toInstant()就是将Date转换成Instant的
14、如何在java8中使用预定义的格式器来将String转换为日期对象
在java8之前,时间日期的格式化非常麻烦,经常使用SimpleDateFormat来进行格式化,但是SimpleDateFormat并不是线程安全的。在java8中,引入了一个全新的线程安全的日期与时间格式器。并且预定义好了很多格式。比如,BASICISODATE格式会将字符串20160414格式化成2016-04-14格式的日期对象.

//String,日期时间的互转
String ds = "20250318";
LocalDate date4 = LocalDate.parse(ds, DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(date4);
String ds1="03 18 2025";
LocalDate date5 = LocalDate.parse(ds1,DateTimeFormatter.ofPattern("MM dd yyyy"));
System.out.println(date5);
//日期转字符串
DateTimeFormatter dtf =DateTimeFormatter.ofPattern("yyyy年MM月dd日");
String ds3 = date5.format(dtf);
System.out.println(ds3);
--------------------------------------------------------------------------------------
2025-03-18
2025-03-18
2025年03月18日
java8中日期与时间API的几个关键点
经过上面的例子,我们已经对java8的时间日期有了一定的了解,现在回顾一下
●它提供了javax.time.ZoneId用来处理时区。
●它提供了LocalDate与LocalTime类
●Java 8中新的时间与日期API中的所有类都是不可变且线程安全的,这与之前的Date与Calendar API中的恰好相反,那里面像java.util.Date以及SimpleDateFormat这些关键的类都不是线程安全的。
●新的时间与日期API中很重要的一点是它定义清楚了基本的时间与日期的概念,比方说,瞬时时间,持续时间,日期,时间,时区以及时间段。它们都是基于ISO日历体系的。
每个Java开发人员都应该至少了解这套新的API中的这五个类:
●Instant 它代表的是时间戳,比如2016-04-14T14:20:13.592Z
●LocalDate 它表示的是不带时间的日期,比如2016-04-14。它可以用来存储生日,周年纪念日,入职日期等。
●LocalTime - 它表示的是不带日期的时间
●LocalDateTime - 它包含了时间与日期,不过没有带时区的偏移量
●ZonedDateTime - 这是一个带时区的完整时间,它根据UTC/格林威治时间来进行时区调整
this关键字
一:概述
this代表它所在函数所属对象的引用。
简单说:哪个对象在调用this所在的函数,this就代表哪个对象。
二、this关键字主要有三个应用:
(1)this调用本类中的属性,也就是类中的成员变量;
Public Class Student {
String name; //定义一个成员变量name
private void SetName(String name) { //定义一个参数(局部变量)name
this.name=name; //将局部变量的值传递给成员变量
}
}
(2)this调用本类中的其他方法;大部分可以省略不写
public class ThisTest {
public static void main(String[] args) {
Dog dog1=new Dog();
dog1.setName("小白");
dog1.aa();
Dog dog2=new Dog();
dog2.setName("小黑");
dog2.aa();
}
}
class Dog{
String name;
public void setName(String name) {
this.name=name; //这个不能呀,不然用的是成员变量名
this.aa(); // 这里可以省略不写this
}
public void aa() {
System.out.println(name);
}
}
(3)this调用本类中的其他构造方法,调用时要放在构造方法的首行。
public class Student { //定义一个类,类的名字为student。
public Student() { //定义一个方法,名字与类相同故为构造方法
this("Hello!"); //要写在第一行,不能写在其它代码的下面
}
public Student(String name) { //定义一个带形式参数的构造方法
this.name=name;
}
}

3901

被折叠的 条评论
为什么被折叠?



