Java之路 - final内部类ObjectDateCalendar

Posted 五号世界

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之路 - final内部类ObjectDateCalendar相关的知识,希望对你有一定的参考价值。

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);
    }
}
View Code

此时案例中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);
    }
}

 

以上是关于Java之路 - final内部类ObjectDateCalendar的主要内容,如果未能解决你的问题,请参考以下文章

java提高篇-----详解匿名内部类 ,形参为什么要用final

[Java开发之路](24)内部类

java中final关键字权限修饰符内部类(成员内部类局部内部类匿名内部类)

Java内部类与final关键字详解

关于java的final接口内部类细节

java基础第九篇之final和内部类等