对于Java这种工具,他还是内置了非常多的轮子,比如之前用到的数学计算,还有日期工具,甚至画图的AWT(废弃),JavaFX等等,轮子非常多,而Java作为面向对象的语言,我们也可以制作自己的类,可以封装成包,把这个类(轮子)给别人.
其中日期时间这个轮子,Java迭代了很多个版本,一路修BUG一路迭代,Java 1.0就有Date类,Java 1.1有Calendar类,但是从Java 8才有LocalDate和Instant.
Date和Calendar出现得比较早,存在1582年问题,如果不涉及这个,到是用起来没差,但是有新的当然还是推荐用新的.Date的使用也比较简单.
package com.hello;
import java.text.SimpleDateFormat;
import java.util.Date;
public class basic {
public static void main(String[] args) {
Date date_now = new Date(); // 创建一个新的日期实例,默认保存的是系统时间
Date date_diff = new Date();
date_diff.setYear(100); // 设置日期实例中的年份
int year = date_diff.getYear() + 1900; // 获取日期实例中的年份
System.out.println("现在date_diff被我设置成" + year + "年");
int compareResult = date_diff.compareTo(date_now);
System.out.println(compareResult);
System.out.println("现在系统时间正处于" + date_now.getMinutes() + "分!");
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(fmt.format(new Date()));
}
}
运行是可以运行了,但是目前Date的所有方法都处于废弃状态,虽然直到现在都还没删除,但是肯定不推荐了.
Calendar也差不多,只是方法上稍微改变,他是一个抽象类,由具体JDK实现,能解决一些Date不能解决的问题,虽然没被标记废弃,但是其实也不推荐,主要还是复杂.
package com.hello;
import java.util.Calendar;
public class basic {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 0);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
System.out.println("现在hour_of_day被我设置成" + hour);
int compareResult = calendar.compareTo(calendar2);
System.out.println(compareResult);
}
}
运行结果
现在hour_of_day被我设置成0
-1
现在主流推荐的是Java 8引入的LocalDate,以前的Date和Calendar工具,在getMonth之类的操作时候,返回1代表2月,返回0代表1月,这对于以前就是写C的人来看,这没什么不对劲的,但是Java毕竟还是简单且优美,所以在LocalDate这个工具上,就改进起来了.整体也简洁了很多.
package com.hello;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class basic {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
LocalDate dateManual = LocalDate.now();
// 获得该日期所在的月份。注意getMonthValue返回的是数字月份,getMonth返回的是英文月份
int month = localDate.getMonthValue();
System.out.println("month=" + month + ", english month=" + localDate.getMonth());
dateManual = dateManual.plusYears(3); // 增加3年
dateManual = dateManual.minusMonths(1); // 减少1月
boolean isBeforeDate = localDate.isBefore(dateManual); // 判断A日期是否在B日期之前,还有isAfter,和isEqual方法
System.out.println("isBeforeDate=" + isBeforeDate);
System.out.println("datetime=" + dateManual.toString()); // 直接可以输出时间
System.out.println("Formatted LocalDateTime: " + localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); // 输出格式化
}
}
在这里时间是没包含在Date里,这才符合理解嘛,Date本来就是日期.如果需要时间和日期组合,可以用这一个来获取,他就是LocalDate和LocalTime的组合体.
LocalDateTime currentDateTime = LocalDateTime.now();
还有Instant这个不能算时间,应该叫时间戳工具,他的精度理论是纳秒的.
package com.hello;
import java.time.Instant;
public class basic {
public static void main(String[] args) {
Instant instant1 = Instant.now();
Instant instant2 = instant1.plusNanos(10);
System.out.println("isBeforeDate=" + instant1.isBefore(instant2));
}
}
所以我们找到一个轮子,知道他的方法,也就能用各种功能,只要我们知道有这个类,有他手册,岂不是可以做各种东西.但是总有没轮子的时候,就需要我们自己做.
我在com.hello下做了一个Fruit的类,暂时先这么设计.
package com.hello;
public class Fruit {
public String name;
public int weight;
public Fruit(){
System.out.println("Fruit");
}
public Fruit(String name, int weight){
this.name = name;
this.weight = weight;
}
public void setName(String name){
this.name = name;
}
public void setWeight(int weight){
this.weight = weight;
}
public String getName(){
return this.name;
}
public int getWeight(){
return this.weight;
}
public String toString(){
return this.name + "的重量" + this.weight;
}
}
这个类包含两个构造方法,如果传递了参数,直接可以赋值一些初始参数,否则需要调用setXXX来设置,最后调用getXXX可以获取属性,直接打印的话等于执行toString方法.来验证下.
package com.hello;
public class basic {
public static void main(String[] args) {
Fruit fruit = new Fruit("苹果",33);
System.out.println(fruit);
fruit.setName("雪梨");
fruit.setWeight(50);
System.out.println(fruit);
}
}
运行结果
看起来符合预期,但是由于我们内部所有东西都是public的,所以不通过指定函数他也能修改.
赶紧把类里面的参数改成private,这样就只能通过我的方法来访问了,IDEA也同时告诉我们他是private就不能直接赋值了.
如果我们再新建一个关于Apple的类别,他依然和其他水果一样有重量,再设计一个感觉就有点多余了,所以我们新建一个Apple类继承于Fruit.
package com.hello;
public class Apple extends Fruit{
private String type;
private String color;
public Apple(){
this.setName("苹果");
}
public void setType(String type) {
this.type = type;
}
public void setColor(String color) {
this.color = color;
}
public String toString(){
return super.toString() + ",同时我是一种苹果,类型是" + this.type + "颜色是" + this.color;
}
}
这里this代表是本类,包括父类,而super就是强调父类了,我的toString会执行父类的toString方法,而苹果本身也有设置类型和颜色的方法,获取方法暂偷懒了...
我们这里实验可不可以在Apple类赋值父类的name呢,而不调用setName.
这里就要说说修饰符了.
- public - 任何人可访问
- 无修饰符 - 友好的,同一个包下的看起来就像public
- protected - 允许本家族访问,就是自己和子类都可以.
- private - 只能自己访问
所以上面name如果要被子类可改,那么他父类必须为protected才可以.
在这个Apple的类中,我们可以修改toString方法,有时候我们并不想什么方法都能被子类修改,比如子类可能额外重写setName,那么怎么办呢,可以使用final终态来标记,final可以标记变量,也可以标记方法,标记变量就有点像C语言的const.
先把Fruit类的setName改成final.
public final void setName(String name){
this.name = name;
}
然后在Apple子类中就不允许再重写他了.
同理如果一个变量是final,他必须赋初值,并且也不能修改.
常规的类就基本这些了,那既然有常规的,也就有不常规的了,比如我现在描述一个树和开花的类,假设只有树能开花,单独开一个类来继承有点繁琐啊.
树有自己的名字,树可以发芽,树可以开花,内部类调用外部要指定类名,因为内部类并不是继承.
package com.hello;
public class Tree {
private String tree_name;
public Tree(String tree_name) {
this.tree_name = tree_name;
}
public void sprout() {
System.out.println(tree_name + "发芽啦");
}
// Flower类位于TreeInner类的内部,它是个内部类
public class Flower {
private String flower_name;
public Flower(String flower_name) {
this.flower_name = flower_name;
}
public void bloom() {
Tree.this.sprout();
System.out.println(Tree.this.tree_name + "的" + flower_name + "开花啦");
}
}
}
测试一下.
package com.hello;
public class basic {
public static void main(String[] args) {
Tree tree = new Tree("桃树");
Tree.Flower flower = tree.new Flower("桃花");
flower.bloom();
}
}
应该能输出[桃树的桃花开花啦],内部类也是要new创建的,如果把内部类增加static的描述,他就成了嵌套类,嵌套类不能访问外部类.
这次正式有意地引入static这个关键词,那么,他是什么意思呢.如果一个变量被标记了static,外部即可通过[类名.属性名]直接访问静态属性,静态方法也是同理,比如之前的Math.sqrt就是静态方法.稍微修改一下,使用终态描述flower_name,因为每次实例化他都会给初值,使用静态修饰flower_count,这样他就每次实例化都是同一个变量,同时使用pulib修饰他,使得公开地方可以访问他.
在basic.java中也添加一个static块包裹的代码,这部分代码在类加载前就执行了.
执行后可以看到,被13行修改后,再开花就变成11朵了,被static修饰的也提前执行了.
如果final static来修饰,最后得到的效果就等效于完全不变常量,而且不会因为创建多次对象而复制.
嵌套类还可以做单例模式,就是某个类只能被实例化一次,不管是饿汉方式还是懒汉方式都不能保证严格的只实例化一次,但是单例模式可以,所以偶尔也会发现有些类的获取方法是getInstance而不是new Class()的大概原因吧.
public class SingletonNest {
// 定义一个嵌套类,并在嵌套类的内部声明当前类的实例
private static class SingletonHolder {
private static SingletonNest instance = new SingletonNest(); // 创建一个外层类的实例
}
// 获取当前类的实例
public static SingletonNest getInstance() {
return SingletonHolder.instance;
}
}
另一种和常量相似的我们叫枚举类,我们直接举例一个java.time.DayOfWeek就是枚举类,他也可以有自己的构造方法,主要表达哪些单纯数字看不出具体意思的,能形成一一对应关系.比如以下代码能输出数字5和FRIDAY字符串.
System.<em>out</em>.println(DayOfWeek.<em>FRIDAY</em>.getValue());
System.<em>out</em>.println(DayOfWeek.<em>FRIDAY</em>.name());
具体枚举类内部可以自行进去看看
解释一下他的构造方法,比如我们使用DayOfWeek.FRIDAY时候,其实是调用了这个类构造方法的DayOfWeek(6),因为他这个刚好是6,比如我写的这个枚举类,当你调用SUMMER时候,其实是给构造方法传递(2,"夏天").
public enum Season {
// 在定义枚举变量的同时,调用该枚举变量的构造方法
SPRING(1, "春天"), SUMMER(2, "夏天"), AUTUMN(3, "秋天"), WINTER(4, "冬天");
private int value; // 季节的数字序号
private String name; // 季节的中文名称
// 在构造方法中传入该季节的阿拉伯数字和中文名称
private Season(int value, String name) {
this.value = value;
this.name = name;
}
// 其他省略
}
今天卷得有点多,就到此打住了.