Java之路 - final、内部类、Object、Date、Calendar
1.final的用法
final表示最终的、不可变的,对于类和方法来说,abstract 和 final 关键字不能同时使用,因为前者是必须要覆盖重写,后者是不能覆盖重写,自相矛盾
(1)可以用来修饰一个类
(2)可以用来修饰一个方法
(3)可以用来修饰一个局部变量
(4)还可以用来修饰一个成员变量
第一种用法: - - 用于修饰类
格式: public final class 类名称
含义:当前这个类不能有任何子类(太监类)
第二种用法: - - 用于修饰方法,这个方法就是最终方法,不能被覆盖重写
格式:修饰符 final 返回值类型 方法名称(){ 方法体 }
第三种用法:- - 用于修饰局部变量
格式:final 数据类型 名称 = 数值; 一但使用final对局部变量进行限制,那么这个变量终生不可改变
什么叫不可变?:对于基本数据类型来说,不可变说的是变量当中的数据不可改变
对于引用类型来说,不可改变说的是变量当中的地址值不可改变
类Myclas: public class MyClass { private String name; public MyClass() { } public MyClass(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 执行类: public class MyDemoFinal { public static void main(String[] args) { final int num = 100; //此时num不可改变了 MyClass c1 = new MyClass("chris"); System.out.println(c1.getName()); System.out.println(c1); c1 = new MyClass("joe"); System.out.println(c1.getName()); System.out.println(c1); } }
此时案例中c1的地址值是发生了改变的
如果在c1之前加入final,地址值就不可改变了
final MyClass c1 = new MyClass("chris"); c1 = new MyClass("joe"); //!!!!错误写法
c1.setName("joe"); //这种是可行的 对其内容是可以改变
第四种用法: - -用于修饰成员变量
(1)由于成员变量具有默认值,所以用了final之后必须手动赋值,才不会给默认值
(2)对于final的成员变量,要么使用直接赋值
(3)要么通过构造方法赋值,必须保证所有重载形式的构造方法都对其进行了赋值,且去掉setter方法
public class MyClass { private final String name; public MyClass() { name = "chris"; } public MyClass(String name) { this.name = name; } public String getName() { return name; } }
2.Java中四种权限修饰符
(1)public > (2)protected > (3)(default) > (4)private
同一个类(我自己) (1)yes (2) yes (3)yes (4)yes
同一个包(我邻居) (1)yes (2) yes (3)yes (4)no
不同包子类(我儿子) (1)yes (2) yes (3)no (4)no
不同包非子类(陌生人)(1)yes (2) no (3)no (4)no
3.内部类(一个类内部包含另个类) 里面的就是内部类
分类:
1.成员内部类
2.局部内部类(包含匿名内部类)
4.成员内部类
格式: 修饰符 class 外部类名称 {
修饰符 class 内部类名称{
//.....
}
//...
}
注意事项:内用外,随意访问; 外用内,需要内部类对象
如何使用成员内部类?有两种方式:
(1)间接方式:在外部类的方法当中,使用内部类;然后main方法调用外部类的方法
外内部类 public class Body { //内部类 public class heart{ public void methodHeart(){ System.out.println("内部类方法"); System.out.println("我叫" + name); // 可以访问外部的成员变量 } } //外部类的成员变量 private String name; public void methodBody(){ System.out.println("外部类方法"); heart heart = new heart(); heart.methodHeart(); } public String getName() { return name; } public void setName(String name) { this.name = name; } } main: public class MyDemoFinal { public static void main(String[] args) { Body body = new Body(); body.methodBody();//通过调用外部类方法使用内部类方法 } }
(2)直接方式:公式: 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
public class MyDemoFinal { public static void main(String[] args) { Body body = new Body(); body.methodBody();//通过调用外部类方法使用内部类方法 //直接访问成员内部类 Body.heart directUse = new Body().new heart(); directUse.methodHeart(); } }
内外部类变量重名时的使用方法:
public class Outer { int num = 10; //外部类成员变量 public class Inner{ int num = 20; //内部类成员变量 public void methodInner(){ int num = 30; //内部类方法的局部变量 System.out.println(num); //输出的是局部变量 System.out.println(this.num); //输出的是内部类成员变量 System.out.println(Outer.this.num);//外部类的成员变量 } } }
5.局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类,只要当前所属的方法才能使用它,出了这个方法外面就不能用了
定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
//....
}
}
}
示例:
public class Outer { public void methodOuter(){ class Inner { //这是个局部内部类 int num = 10; public void methodInner(){ System.out.println(num); } } Inner in = new Inner(); in.methodInner(); } }
局部内部类如果希望访问所在方法的局部变量,那么这个变量必须是有效【final】的,从java8开始,只要局部变量事实不变,既不被改变,那么final关键字可以省略 - - - 和生命周期有关
情况:
public class Outer { public void methodOuter(){ int num = 10; class Inner { //这是个局部内部类 public void methodInner(){ System.out.println(num); } } } }
原因:(1)new出来的Inner对象在堆内存当中,
(2)而局部变量num是跟着方法走的,在栈内存当中
(3)methodOuter方法运行结束之后,立刻出栈,局部变量就会立刻消失
(4)但new出来的对象Inner会在堆当中持续存在,直到垃圾回收消失
(5)所以这个num只要保证不会变,既有效final,那么它就会被复制到常量池中,这就解决了生命周期的问题
6.定义一个类时,权限修饰符的规则
(1)外部类:public / (default)
(2)成员内部类:public / protected / (default) / private
(3)局部内部类:什么都不能写
7.匿名内部类 重点
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】
定义格式:
接口名称 对象名 = new 接口名称 {
这里面覆盖重写所有抽象方法
};
注意事项:
(1)匿名内部类在创建对象的时候,只能使用唯一一次,如果想再new,必须再重写方法
(2)匿名对象无法调用第二次方法
匿名内部类的匿名对象用法 new MyInterface() { @Override public void methodAbs1() { System.out.println("1111"); } @Override public void methodAbs2() { System.out.println("2222"); } }.methodAbs1();
(3)匿名内部类是省略了【实现类/子类】,但是匿名对象是省略了【对象名称】
8.Object类
类Object是类层次结构的根(最顶层)类,每个类都使用Object作为超(父)类
(1)Object类 的 toString方法 默认打印的是对象的地址值
直接打印对象的名字,其实就是调用对象的toString方法
public class DemoOtherClass { public static void main(String[] args) { Person p = new Person("chris",21); String s = p.toString(); System.out.println(s); System.out.println(p); } }
因此这样打印没有什么意义,我们要在定义的类中重写toString方法
public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; }
(2)Object类 的 equals方法 指示其他某个对象是否与此对象‘相等’
Object类中equals方法的源码:
public boolean equals(Object obj) { return (this == obj); }
参数为引用类型时,比较的是双方的地址值
public class DemoOtherClass { public static void main(String[] args) { Person p1 = new Person("chris",21); Person p2 = new Person("joe",20); boolean result = p1.equals(p2); System.out.println(result); System.out.println(p1.equals(p2)); } }
我们可以重写equals方法来判断两个对象的属性是否一致,以后用快捷键添加就好了
因为equals接收的是Object类型,所以此处隐含着一个多态 Object obj = p1 = new Person("chris",21)
我们又知道多态有一个弊端:无法使用子类特有的属性和方法 因此此时要使用向下转型进行强转
重写的equals方法:
@Override public boolean equals(Object obj){ //增加一个判断,如果传过来的参数obj是this本身,直接返回true if(obj == this){ return true; } //增加一个判断,如果传过来的参数为null,直接返回false,提高程序的效率 if (obj == null){ return false; } //先增加一个判断,判断obj是否是person类型才能转换,比如random,可以防止ClassCastException if (obj instanceof Person){ //使用向下转型 Person p = (Person) obj; //比较两个对象的属性;一个是调用方法的this(p1),一个是p(obj=p2) 此时在使用字符串方法中的equals方法来比较 boolean b = this.name.equals(p.name) && this.age == p.age; return b; } //不是Person类型,就直接返回false return false; }
9.Objects类
Objects类的提供的equals方法是静态方法,通过类直接调用,可以防止空指针异常
源码:
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
10.Date类 表示特定的瞬间,精确到毫秒(long类型)
毫秒:千分之一秒 1000毫秒 = 1 秒
特定的瞬间:一个时间点,一刹那的时间 2020-04-16 11:04:24:333 这就是一个瞬间
毫秒值的作用:可以对时间和日期进行计算
如:在对2044-02-03 到 2050-05-22 号之间有多少天 可以将日期转换为毫秒(就是计算当前日期到时间原点之间一共经历了多少毫秒),计算完后再把毫秒转换为日期
时间原点(0毫秒):1970年1月1日 00:00:00(英国格林威治)
注意:中国属于东八区,会把时间增加8小时
(1)空构造方法 获取当前的时间
public class DemoOtherClass { public static void main(String[] args) { System.out.println(System.currentTimeMillis());//获取当前系统时间到时间原点经历了多少毫秒 methodDate(); } public static void methodDate(){ Date date = new Date(); System.out.println(date); } }
(2)带参数的构造方法 long类型表示的就是传递毫秒值,把毫秒转换为Date日期
public static void methodDate(){ Date date1 = new Date(); System.out.println(date1); Date date2 = new Date(0L); System.out.println(date2); }
此时date2的输出值为:Thu Jan 01 08:00:00 CST 1970
(3)成员方法Long getTime() 返回自1970 年 。。。 以来此对象表示的毫秒值 相当于System.currentTimeMillis()
(4)String toLocalString 根据本地格式转换为日期对象
public class CalendarPratise { public static void main(String[] args) { Date data = new Date(); System.out.println(data.toLocaleString()); //当前电脑下的时间 } }
11.DateFormate类 将Date对象转换为String对象 它是一个抽象类,可以用它的子类SimpleDateFormat
它是日期/时间格式化子类的抽象类;
子类SimpleDateFormat的方法:
(1)成员方法 String format(Date date):按照指定的模式,把Date日期,格式化为符合模式的字符串
(2)成员方法 Date parse(String source):把符合模式的字符串解析为Date日期
SimpleDateFormat
构造方法:SimpleDateFormat(String Pattern) 传入模式
参数:传递指定的模式
模式:区分大小写 y - 年 M - 月 d - 日 H - 时 m-分 s - 秒 如:"yyyy-MM-dd HH-mm-ss" 连接符号可以改变
format方法:
private static void demo01() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"); Date data = new Date(); String timeText = sdf.format(data); System.out.println(data); System.out.println(timeText); //输出的是按照构造方法中指定的模式的字符串 }
parse方法:将符合构造方法中模式的字符串解析为Date日期
private static void demo01() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"); Date date = sdf.parse("2020-04-16 13-28-08"); System.out.println(date); }
练习:输入出生年月日,获取你已经出生了多少天
public class DatePratise { public static void main(String[] args) throws ParseException { Scanner s = new Scanner(System.in); System.out.println("请输入您的生日,格式为yyyy-MM-dd"); String birthday = s.next(); //将获取的生日解析为Date格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date dateBirthday = sdf.parse(birthday); //将Date转换为毫秒值 long seconds = dateBirthday.getTime(); //获取当前的日期转换为毫秒值 Long timeNow = new Date().getTime(); //两个时间相减 now - past long time = timeNow - seconds; //将差值转换为天 (s/1000/60/60/24) System.out.println("你已经出生了" + time/1000/60/60/24 +"天"); } }
12.Calendar日历类 是一个抽象类 无法直接创建对象使用,里面有一个静态方法getInstance,该方法返回了其子类对象
Calendar calendar = Calendar.getInstance(); //多态
成员方法:
参数: int field:日历类的字段,可以使用Calendar类的静态成员变量获取
(1)get(int field)方法
通过制定日历字段,返回给定日历字段的值
public class CalendarPratise { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); //多态 int year = calendar.get(Calendar.YEAR); System.out.println(year); int month = calendar.get(Calendar.MONTH); System.out.println(month); //西方的月份为0-11月 所以我们要加一个月 int date = calendar.get(Calendar.DATE); System.out.println(date); } }
(2)set(int field,int value)方法
将给定的日历字段设置为给定值
public class CalendarPratise { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); //多态 calendar.set(Calendar.YEAR,2050); calendar.set(2012,3,4); //重载方法同时对三个值进行设置 } }
(3)add(int field,int amount)方法
根据日历的规则,为给定的日历字段增加或减少指定的值
public class CalendarPratise { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); //多态 calendar.add(Calendar.YEAR,2);//将年份增加两年 } }
(4)public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象
把日历对象转为日期对象
public class CalendarPratise { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); //多态 Date time = calendar.getTime(); System.out.println(time); } }