Java第十一课_内部类,Object类,枚举和异常

发布时间 2023-12-15 22:12:42作者: 张心野

1.内部类

  • 一般内部类

    public class Pratice {
    
        public static void main(String[] args) {
            /*
                内部类 : 描述事物内部的事物 ; 就是一个类定义在另一个类的内部
    
                当内部类定义在成员变量的位置上时,可以被成员修饰符修饰,修饰后会具备修饰符的特征
                    1.private 只能在当前类中访问. 推荐
                    2.static 访问出现局限性. 此时如果要访问外部类的成员, 需要将外部成员也静态化
             */
            // 间接访问 :
            Outer outer = new Outer();
            outer.function();
    
            // 直接访问(了解) :
            // 不建议. 内部类一般有特有用途, 推荐通过外部类间接使用. 最好给内部类加上private修饰符
            Outer.Inner inner = new Outer().new Inner();
            inner.show();
        }
    }
    
    public class Outer {
    
        private int num = 110;
    
        class Inner{// 最好加上private修饰符.    加static的话, 如果要访问num, 需要将num也静态化.
    
            public void show(){
                // 内部类可以访问所在外部类的所有成员,包括私有成员在内
                System.out.println("num = " + num);
            }
        }
    
        public void function(){
            // 外部类要访问内部类,必须创建对象
            Inner inner = new Inner();
            inner.show();
        }
    }
    
  • 局部内部类

    public class Pratice {
    
        public static void main(String[] args) {
            int x = 10;
            int y = 10;
    
            // 局部内部类 : 定义在局部位置的内部类,不能被成员修饰符修饰
            //     被局部内部类使用的外部变量, 一开始就会自动变为final.
            //         原因是函数调用完就结束,类不行. 内部类和外部类没有区别. 但局部内部类用了函数的局部变量, 之后处理很复杂, 于是强制转final.
            //         https://www.runoob.com/w3cnote/inner-lambda-final.html
            class Inner {
    
                public void show(int z) {
                    System.out.println("inner show");
                    System.out.println("x = " + x);
                    System.out.println("z = " + z);
                    //x += 1;error
                    z += 1;
                    System.out.println("z = " + z);
                }
            }
            y = 11;
            Inner inner = new Inner();
            inner.show(3306);
            inner.show(1521);
            inner.show(8080);
        }
    }
    
  • 匿名内部类

    public class Practice {
    
        public static void main(String[] args) {
            /*
                匿名内部类 : 没有名字的内部类
                本质 : 就是一个有点胖的匿名对象
                格式 :   new  父类/接口(){} ;
             */
            Computer computer = new Computer();
            computer.run();
    
            // 听音乐
            computer.function(new LoudspeakerBox());// 正常写法
    
            computer.function(new USB(){// 匿名内部类, 不再额外创建一个类, 直接写类
    
                @Override
                public void start() {
                    System.out.println("摄像头开启");
                }
    
                @Override
                public void close() {
                    System.out.println("摄像头关闭");
                }
            });
            // lambda编程风格 : 面向函数   ->  : lambda运算符
        }
    }
    
    public class Computer {
    
        public void run(){
            System.out.println("电脑开始运行了....");
        }
    
        public void function(USB usb){
            usb.start();
            usb.close();
        }
    }
    
    public interface USB {
        void start();
        void close();
    }
    
    public class LoudspeakerBox implements USB{
        @Override
        public void start() {
            System.out.println("音箱启动");
        }
    
        @Override
        public void close() {
            System.out.println("音箱关闭");
        }
    }
    

2.Object类

  • Object类常用函数

    public class Practice {
    
        public static void main(String[] args) throws ClassNotFoundException {
            /*
                Object类 :
                    Object是类层次结构的根。
                    每个类都使用Object作为超类/父类/根类。
                    包括数组在内的所有对象都实现此类的方法
                    主要有以下函数,以及Practice02中的equals​函数
             */
            // clone()
            //      创建并返回此对象的副本。
    
            // void	finalize() JDK9 过时的 ,  不推荐使用
            //      GC 进行垃圾回收前会调用该函数
            A a = new A();
            a = null;
            System.gc();// 通知GC进行垃圾回收
    
            // Class<?>	getClass()
            //              返回此对象的运行时类。其实就是字节码文件的对象
            // 获取字节码文件对象的三种方式 :
            // 1. Object  : Class<?>	getClass()
            a = new A();
            Class<?> clazz1 = a.getClass();
            System.out.println("clazz1 = " + clazz1);
            // <> : 泛型
            // 2. 所有的类型都有一个静态的默认的属性 :   .class
            Class<?> clazz2 = A.class;
            System.out.println("clazz2 = " + clazz2);
            System.out.println("int.class = " + int.class);
            System.out.println("void.class = " + void.class);
            System.out.println("---------------------------------");
    
            // 3. Class : public static Class forName(String 全类名)
            Class<?> clazz3 = Class.forName("com.msr.lesson04.A");
            System.out.println("clazz3 = " + clazz3);
    
            // int	hashCode()
            //          返回对象的哈希代码值/地址值。
            System.out.println(a.hashCode());// 10进制地址值
            System.out.println("a = " + a);// 16进制地址值
    
            System.out.println("----------------------------------------");
            // String	toString()
            //          返回对象的字符串表示形式。
            System.out.println(a);
            System.out.println(a.toString());
    
            Person person = new Person();
            System.out.println(person);
            System.out.println(person.toString());
            System.out.println(person.getClass().getName() + "@@" + Integer.toHexString(person.hashCode()));
            System.out.println(clazz1.getName());
    
            // void	notify()
            //              唤醒正在等待此对象的 监控。
            // void	notifyAll()
            //              唤醒在此对象的监视器上等待的所有线程。
            // void	wait()
            //          使当前线程等待,直到它被唤醒,通常 通过被通知或中断。
            // void	wait​(long timeoutMillis)
            //          使当前线程等待,直到它被唤醒,通常 通过被通知或打断,或直到 一定数量的实时时间已经过去。
            // void	wait​(long timeoutMillis, int nanos)
        }
    }
    
    public class Practice02 {
    
        public static void main(String[] args) {
            // boolean	equals​(Object obj)
            //       默认使用 == 进行比较, 只能比较地址值. 一般需要重写, 来比较内容是否一致
            Person person1 = new Person("lisi", 20);
            Person person2 = new Person("lisi", 20);
    
            System.out.println(person1 == person2);// false
            System.out.println(person1.equals(person2));// 不重写比较地址值,就会报错
    
            String s1 = new String("abc");
            String s2 = new String("abc");
    
            System.out.println(s1 == s2);//
            System.out.println(s1.equals(s2));// String已经经过重写, 比较内容
    
            // 枚举 :
            // 学生系统
            // 常用类/Throwable
        }
    }
    
    public class A {
    
        @Override
        protected void finalize() throws Throwable {
            System.out.println("呀 被回收了....");
        }
    }
    
    public class Person {
    
        private String name;
        private Integer age;
    
        public Person() {
        }
    
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public boolean equalsByName(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return Objects.equals(name, person.name);
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Person person = (Person) o;
            return Objects.equals(name, person.name) &&
                    Objects.equals(age, person.age);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    

3.枚举

  • 枚举

    public class Practice {
        /*
            枚举 : 是一个特殊的类 , 对象是有限个并且是不可变的(jdk5.0开始支持)
                自带创建好的类对象数组, 通过类名直接访问, 不推荐创建类对象, 或修改类数组
         */
        public static void main(String[] args) {
            System.out.println(Season.AUTUMN);
    
            Season[] seasons = Season.values();// 拿到枚举对象(类数组)
            for (Season season : seasons) {
                System.out.println(season + " ------ " + season.getName() + " :: " + season.getDesc());
            }
    
            System.out.println("---------------------------------");
            Season season = Season.valueOf("SPRING");// 拿到指定的类变量
            System.out.println("season = " + season);
        }
    }
    
    public enum Season {
        /*
            除了enum声明, 和正常类写法基本一致
            直接A,A()来创建对象
                A是调用空参构造器,A()是调用相应的带参构造器
                格式是A,B,C,D; 在最后一个对象前都是, 结尾;代表对象创建完了
         */
        SPRING("春天", "春风又绿江南岸"),
        SUMMER("夏天", "映日荷花别样红"),
        AUTUMN("秋天", "秋水共长天一色"),
        WINTER("冬天", "千里冰封,万里雪飘");
    
        private String name;
        private String desc;
    
        // 枚举中的构造器都是私有
        Season(String name, String desc) {
            this.name = name;
            this.desc = desc;
        }
    
        public String getName() {// 推荐只创建get函数
            return name;
        }
    
        public String getDesc() {
            return desc;
        }
        @Override
        public String toString() {
            return "Season{" +
                    "name='" + name + '\'' +
                    ", desc='" + desc + '\'' +
                    '}';
        }
    }
    
    public class Practice02 {
    
        public static void main(String[] args) {
            // Thread类 源码中的 1747 行  内部枚举类
            // 线程的状态 :
            // Thread :
            Thread.State[] states = Thread.State.values();
            for (Thread.State state : states) {
                System.out.println("state = " + state);
            }
    
        }
    }
    

4.异常

  • 异常体系

    public class Practice {
    
        public static void main(String[] args) {
            /*
                异常体系 :
                Throwable : 是Java语言中所有错误和异常的超类
                    |Error/错误 : 过于严重,无法解决
                    |Exception/异常 : 可能出现的异常, 需要考虑解决方法
                        |RuntimeException/运行时异常 : 代码执行过程中才出现的异常 , 比如 NullPointerException
                        |RuntimeException之外的异常/编译时异常 : 编译时期就会提示,比如 ClassNotFoundException
    
                只有作为此类实例(或其子类之一)的对象才会由Java虚拟机抛出,
                或者可以由Java抛出语句抛出。
                类似地,只有这个类或它的一个子类可以是catch子句中的参数类型。
                为了在编译时检查异常,Throwable和任何不属于RuntimeException或Error子类的Throwable子类都被视为已检查异常。
             */
            /*
                异常处理基本格式 :
                try{
                    可能发生异常的代码;
                }catch(异常类型  异常名){
                    异常发生时的处理方式;
                }finally{
                    一定要被执行的代码;
                }
    
                注意 :
                    1.有异常但没有异常处理结构时 :
                        程序会终止在异常发生的那一行
                    2.有异常并且有对应异常处理结构时 :
                        程序在异常产生的位置开始跳转 ----> catch语句
                            ----> finally语句
                            ----> 程序正常运行
                    3.有异常并且有异常处理结构 , 但是没有对产生的异常进行处理时 :
                        程序在异常产生的位置开始跳转 ----> finally语句
                            ----> 程序打印异常信息
                            ----> 程序终止
                    4.有 处理方式 , 但没有异常产生 :
                        try语句执行完后  -->
                        finally语句 --->
                        程序正常运行
    
                    finally语句都会执行,除非在这之前就被 System.exit(0); 命令给结束掉程序
                    finally 不是必须的, 内容常为内存释放, 比如 : scanner.close();
             */
            /*
                多异常的处理时
                try{
                }catch(异常类型  异常名){
                }catch(异常类型  异常名){
                }...........
                finally{
                }
                    当一个try对应多个catch语句时 , catch语句中的父类异常要写到后面
             */
            Demo demo = new Demo();
            try{
                int x = demo.div(10, 0);
                System.out.println("x = " + x);
            }catch (ArithmeticException | NoSuchElementException e){
                System.out.println("除数不能为0");
            }catch (NullPointerException e) {
                System.out.println("null指针 : 有变量为null 啦!!");
            } catch (Exception e) {
                System.out.println("抓住老大!!");
            }finally {
                System.out.println("finally 一定会被执行");
            }
            System.out.println("over");
        }
    }
    
    public class Demo {
        public int div(int a,int b){
            return a / b;
        }
    }
    
  • throw及异常信息输出

    public class Practice {
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            int x = 0;
            try {
                x = demo.div(10, 0);
            } catch (Exception e) {
                // String	getMessage() : 获取异常产生的原因
                System.out.println("e.getMessage() = " + e.getMessage());
                System.out.println("-----------------------------------------------");
                // String	toString() : 异常名称 + 异常原因
                System.out.println("e.toString() = " + e.toString());
                System.out.println("-----------------------------------------------");
                // void	printStackTrace() : 打印异常的原因 , 异常名称 以及异常产生的位置
                //                  该方法也是 JVM 的默认处理方法
                e.printStackTrace();
            }
            System.out.println("x = " + x);
            System.out.println("over");
    
            // https://zhuanlan.zhihu.com/p/558991703
            // 总结参考 : https://blog.csdn.net/weixin_46756314/article/details/122360486
        }
    }
    
    public class Demo {
        /*
            throw : 判定可能会出问题, 主动抛出一个异常
                要求调用者必须使用try-catch语句, 处理可能出现的异常
                这种情况下异常被处理, 不会打断程序运行.
    
                如果抛出的是一个编译时异常,需要在函数部分进行声明 ;
                如果抛出的是一个运行时异常,不需要在函数部分进行声明 ;
    
            throws : 名次 , 用于函数声明部分 , 声明异常
        */
        public int div(int a, int b) throws Exception {
            if (b == 0) {
                throw new Exception("除数不能为 0 !");// 输出异常信息
            }
            return a / b;
        }
    }