Day04-Java面向对象下


title: Day04-Java面向对象下
date: 2020-05-29 11:00:03
author:子陌


面向对象

内部类

1、概述

  • 将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)。
  • 访问特点
    • 内部类可以直接访问外部类中的成员,包括私有成员。
    • 而外部类要访问内部类中的成员必须要建立内部类的对象。
class Outer{
    
    
    private int num = 3;
    // 内部类
    class Inner{
    
    
        void show(){
    
    
            System.out.println("show run..." + num);
        }
    }
    public void method(){
    
    
        Inner in = new Inner();
        in.show();
    }
}
// 修饰符
class Outer1{
    
    
    private static int num = 3;
    // 静态内部类
    static class Inner{
    
    
        void show(){
    
    
            System.out.println("show run..." + num);
        }
        static void show1(){
    
    
            System.out.println("show1 run..." + num);
        }
    }
}

//  --- >> 生成   Outer.class、 Outer$Inner.class
class InnerClassDemo{
    
    
    Outer out = new Outer();
    out.method();		// show run...3
    
    // 直接访问外部类中的内部类中的成员
    Outer.Inner in = new Outer().new Inner();
    in.show();
    
    // 如果内部类是静态的,相当于外部类,一加载就存在
    Outer1.Inner in = new Outer1.Inner();
    in.show();
    
    // 如果内部类是静态的,成员是静态的
    Outer.Inner.show1();
}

​ 一般用于类的设计:分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容。这时就是还有的事物定义成内部类来描述

注意事项:如果内部类存在静态成员,该内部类必须是静态修饰

2、内部类访问外部类成员

/*
*	为什么内部类能直接访问外部类中的成员?
*	因为内部类持有了外部类的引用。  外部类名.this
*/
class Outer{
    
    
    int num = 3;
    class Inner{
    
    
        int num = 4;
        void show(){
    
    
            int num = 5;
            System.out.println(num);				// 5
            System.out.println(this.num);			// 4
            System.out.println(Inner.this.num);		 // 5
            System.out.println(Outer.this.num);		// 3
        }
    }
    void method(){
    
    
        new Inner().show();
    }
}
class InnerClassDemo{
    
    
    public static void main(String[] args){
    
    
        new Outer().method();
    }
}

3、内部类-局部内部类

局部内部类在局部位置上只能访问局部中被final修饰的局部变量

/*内部类可以存放在局部位置上*/
class Outer{
    
    
    int num = 3;
    
    Object method(final int zz){
    
    
        int x = 9;		
        final int x1 = 10;									 // final 修饰完终身为10 不会变化 
        // 局部内部类
        class Inner{
    
    
            void show(){
    
    
                System.out.println("show ... " + num + x);		// err 无法访问
                System.out.println("show ... " + num + x1);		// ok 修饰为最终的,如果不是final变量,方法体结束,变量释放,对象还存在
                System.out.println("show ... " + num + zz);		// 如果不加final 也不能访问
            }
        }
        Inner in = new Inner();
        in.show();
        return in; 		// 假设我们返回对象,如果不是最终的,变量结束就会释放,内容修改,final就是常量
    }
    // Inner in = new Inner();	// 无法在局部代码块外访问
}
class InnerClassDemo{
    
    
    public static void main(String[] args){
    
    
        new Outer().method();
    }
}

4、匿名内部类

  • 就是内部类的简写
    • 前提:
      • 内部类必须继承或者实现一个外部类或者接口
    • 格式:
      • new 父类or接口() {子类内容}
abstract class Demo{
    
    
	abstract void show();
}
class Outer{
    
    
    int num = 4;
    /*class Inner {
        void show(){
            int num = 5;
            System.out.println(num);				// 5
        }
    }*/
    void method(){
    
    
        // new Inner().show();
        // 匿名内部类 new父类
        new Demo(){
    
    
            // new Demo 子类对象
            void show(){
    
    
                System.out.println("hhhhhhhhhhhhhhh" + num);
            }
            // void hh(){}   // ok
        }.show();
    }
}
abstract interface Inter{
    
    
    public void hh1();
    public void hh2();
}
class InnerClassDemo{
    
    
    public static void main(String[] args){
    
    
        new Outer().method();
        hhh(new Inter(){
    
    
            public void hh1(){
    
    
                
            }
            public void hh2(){
    
    
                
            }
        });
    }
    public static void hhh(Inter in){
    
    
        in.hh1();
        in.hh2();
    }
}

通常使用场景:

​ 当函数参数是接口类型时,而且接口中的方法不超过三个,可以用匿名内部类作为实际参数传递

对象初始化过程:

class Fu{
    
    
    int num = 8;
    Fu(){
    
    
        // 此处调用super 是Object
        // 显示初始化
        // 构造代码块初始化
        System.out.println("fufuff");
        show();
    }
    void show(){
    
    
        System.out.println("fu hehe" + num);	// 被覆盖
    }
}
class Zi extends Fu{
    
    
    int num = 9;
    {
    
    
        System.out.println("construct code....." + num);
        num = 10;
    }
    Zi(){
    
    
        // super();			// 这边优先构建父类函数 此时 num = 0; 默认隐藏自动执行
        // 显示初始化   	num=9,在父类构造之后
        // 构造代码块初始化
        System.out.println("haha....." + num);
    }
    void show(){
    
    
		System.out.println("zi hehe.." + num);
    }
}
class Test{
    
    
    public static void main (String[] args){
    
    
        /*	执行流程:
         *	1: 进Zi()构造函数
         *	2: 先执行父类构造函数Fu()
         *	3: 父类show()方法被覆盖,执行子类,此时num默认值为0
         * 	4: 进行显示初始化,num = 9
         * 	5: 然后进行构造代码块初始化语句  输出num = 9,重新修改为num = 10
         *	6: 输出子类num = 10
         */
        Zi zi = new Zi();		// 1.fufuff 2.zi hehe..0  3.construct code...9  4.haha.....10
    }
}

练习:

class TD{
    
    
    int y = 6;
    class Inner{
    
    
        static int y = 3;		// 这边会编译失败,内部类不能定义静态成员
        void show(){
    
    
            System.out.println(y);
        }
    }
}
class TC{
    
    
    public static void main(String[] args){
    
    
        TD.Inner ti = new TD().new Inner();
        ti.show();
    }
}

异常处理

  • 概念:Java通过面向对象思想将问题封装成了对象,用异常类对其进行描述。将正常流程的代码和问题处理代码分离,提高阅读性。
  • 在Java中用类的形式对不正常的清空进行描述和封装对象,描述不正常情况的类,称之为异常类。

异常:在运行时期发生的不正常的清空。。。。

1、异常的体系(两大类)

  • Throwable:

    • Error(不可处理)
      • 通常出现重大问题:如运行的类不存在或者内存溢出等。
      • 不编写针对代码对其处理
      • 特点:是由JVM抛出的严重性问题,直接修改程序
    • Exception(可以处理)
      • 在运行时运行出现的一些情况,可以通过try catch finally
  • Exception和Error的子类名都是以父类名作为后缀

该体系特点就在于Throwable及其所有子类都具有可抛性(通过两个关键字throws,throw)

class Demo{
    
    
    public int method(int[] arr, int index){
    
    
        if(arr == null){
    
    
            throw new NullPointerException("数组引用不能为空,你怎么学的!");
        }
        if(index >= arr.length){
    
    
            throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标越界了,你疯了:" + index);
        }else if(index >= arr.length){
    
    
            // 负数脚标用越界抛出异常不太合理,java没提供方法,我们可以自己写,封装成对象
            throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标负了,你真疯了:" + index);
        }
        return arr[index];
    }
}
class ExceptionDemo{
    
    
    public static void main(String[] args){
    
    
        int[] arr = new int[3];
        Demo d = new Demo();
        d.method(arr, 30);
    }
}

2、自定义异常

java没提供异常方法,我们可以自己写,通过异常封装思想,封装成对象。如负数脚标。

注意:如果让一个类成为异常类,必须要继承异常体系,只有具备异常体系才具有可抛性。

自定义异常时:要么继承Exception,要么继承RuntimeException。

// 异常处理声明
// class FuArrayIndexException extends RuntimeException{	// 如果是该继承则不进行检测,无需throws FuArrayIndexException
class FuArrayIndexException extends Exception{
    
    
    FuArrayIndexException(){
    
    
        
    }
    FuArrayIndexException(String msg){
    
    
        super(msg);
    }
}
class Demo{
    
    
    // 声明抛出 public int method(int[] arr, int index) throws FuArrayIndexException{}  同时调用main也要声明丢给虚拟机
    public int method(int[] arr, int index) throws FuArrayIndexException{
    
    
        if(index >= arr.length){
    
    
            throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标越界了,你疯了:" + index);
        
        }else if(index < 0){
    
    
            // 负数脚标用越界抛出异常不太合理,java没提供方法,我们可以自己写,封装成对象
            //throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标负了,你真疯了:" + index);
           
            throw new FuArrayIndexException();
            throw new FuArrayIndexException("大兄弟,数组下标负了,你真疯了:" + index);
        }
        return arr[index];
    }
}
class ExceptionDemo{
    
    
    public static void main(String[] args) throws FuArrayIndexException{
    
    
        int[] arr = new int[3];
        Demo d = new Demo();
        d.method(arr, 30);
    }
}

3、异常的分类:

  1. 编译时被检测异常:只要算Exception和其子类都是,除了特殊子类RuntimeException体系

    出现这种问题:可以进行针对性的处理

  2. 编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类

    出现这种问题:这种问题发生,无法让功能继续进行,更多是调用原因或引发内部状态导致改变,这种问题一般不处理。

    运行时,让调用者调用时程序强制停止,让调用者对代码进行修正

  • throw和throws的区别:

    1. throws使用在函数上

      throw使用在函数内

    2. throws抛出的是类异常,可以抛出多个,用逗号隔开。

      throw抛出的是异常对象

4、异常处理的捕捉形式:

这是可以对异常进行针对性处理的方式

具体格式为try{需要被检测异常的代码} catch(异常类 变量){处理异常的代码} finally{一定会被执行的代码 }

class FuArrayIndexException extends Exception{
    
    
    FuArrayIndexException(){
    
    
        
    }
    FuArrayIndexException(String msg){
    
    
        super(msg);
    }
}
class Demo{
    
    

    public int method(int[] arr, int index) throws FuArrayIndexException{
    
    
        if(arr == null)
            throw new NullPointerException("没有任何数组实体")
 		if(index < 0){
    
    
            throw new FuArrayIndexException("数组下标负了:" + index);
        return arr[index];
    }
}
class ExceptionDemo{
    
    
    public static void main(String[] args) {
    
    
        int[] arr = new int[3];
        Demo d = new Demo();
        try{
    
    
        	int num = d.method(arr, -30);
            System.out.println("num = " + num);
        }catch(FuArrayIndexException e){
    
       // FuArrayIndexException e = FuArrayIndexException();
            System.out.println("msg--" + e.getMessage());		// msg--数组下标负了:-30
            System.out.println("string--" + e);		// 默认调用e.toString()   string:FuArrayIndexException:数组下标负了:-30
            e.printStackTrace();	// JVM的默认异常处理机制就是调用异常对象的这个方法
            System.out.println("负数脚标异常");
        }catch(NullPointerException e){
    
    
            System.out.println(e.toString());
        }catch(Exception e){
    
    
            // 接收所有异常,如果放到第一个,下面的所有异常无效,编译失败,一般不这么处理,让他挂掉发现其他问题
        }// ...... cache(){}
        finally{
    
    
            // 通常用于关闭资源(释放)资源
            // 假设cache异常中return了,那么main doing...不会执行,而finally会继续执行
            System.out.println("finally....");
        }
        // 处理完继续运行,一定会执行的代码,除非退出虚拟机 System.exit(0);
        System.out.println("main doing....");
    }
}

5、异常处理的原则

  1. 函数内容如果抛出需要检测的异常,那么函数上必须要声明

    否则必须要在函数内try-catch捕捉,否则编译失败

  2. 如果调用到了声明异常的函数,要么try-catch要么throws,否则编译失败

  3. 什么时候catch,什么时候throws

    功能内容可与解决,用catch,解决不了,用throws告诉调用者,由调用者处理

  4. 一个功能如果有多个异常,那么调用时,必须有对应多个catch进行针对性的处理

    内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个

6、异常的注意事项

  1. 子类在覆盖父类的方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类
  2. 如果父类抛出多个异常,那么子类只能抛出父类异常的子集
  3. 简单理解:子类覆盖父类方法只能抛出父类的异常或者子类或者子集
  4. 注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛

Object类

所有类的根类,是不断抽取而来的,具备着所有对象都具备的共性内容。

**equals()方法:**一般都会覆盖此方法,根据对象的特有内容,建立判断对象是否相同的依据。

class Person{
    
    
    private int age;
    Person(int age){
    
    
        this.age = age;
    }
    public boolean equals(Object obj){
    
    
        if(obj instanceof Person){
    
    
            Person p = (Person)obj;
            return age == p.age;
        }
        throw new RuntimeException("类型错误");
        return false;
    }
}
class Demo{
    
    }
class ObjectDemo{
    
    
    Person p1 = new Person(20);
    Person p2 = new Person(20);
    Person p3 = p1;
    // 未重载equals方法
    System.out.println(p1 == p2);	    // false
    System.out.println(p1.equals(p2));  // false
    System.out.println(p1.equals(p3));	// true
    Demo d = new Demo();
    System.out.println(p1.equals(d));	// false
    
    // 重载/覆盖equals方法
    System.out.println(p1.equals(p2));  // true
}

**hashCode()方法:**一般重写equals()方法时有必要重写hashCode(),维护方法常规协定

class Person{
    
    
    private int age;
    Person(int age){
    
    
        this.age = age;
    }
 	hashCode(){
    
    
        return age;
    }
}
class ObjectDemo{
    
    
    Person p1 = new Person(60);
    System.out.println(p1.hashCode());  // 3c
    Person p2 = new Person(29);
    
    Class clazz1 = p1.getClass();
    Class clazz2 = p2.getClass();
    System.out.println(clazz1 == clazz2);		// true
    System.out.println(clazz1.getName());		// Person
}

**toString()方法:**放回对象的字符串形式

class Person{
    
    
    private int age;
    Person(int age){
    
    
        this.age = age;
    }
    public String toString(){
    
    
        // return getClass().getName() + "@"+Integer.toHexString(hashCode());
        return "Person:" + age;
	}
}
class ObjectDemo{
    
    
    Person p1 = new Person(60);
    Person p2 = new Person(29);
    
    System.out.println(clazz1 == clazz2);		// true
    System.out.println(clazz1.getName());		// Person
}

包(package)

  • 对类文件进行分类管理
  • 给类提供多层命名(名称)空间
  • 写在程序文件的第一行
  • 类名的全程是:包名.类名
  • 包也是一种封装形式

包名规范:所有名字都小写

编译自动生成包名:javac -d . PackageDemo.java

package packa;
class DemoA{
    
    
    public void show(){
    
    
        System.out.println("demo A");
    }
    protected void pta(){
    
    		// protected 保护的权限,只有继承才能使用
        System.out.println("hahahaha");
    }
}
package packb;
class DemoB extends packa.DemoA{
    
    	// 不同包中类名不能直接访问,需要加包名才可以访问
    public void method(){
    
    
        System.out.println("demo B");
    }
}
package mytest;
class PackTestDemo{
    
    
    public static void main(String[] args){
    
    
        packa.DemoA d = new packa.DemoA();
        d.show();
        
        packb.DemoB b = new packb.DemoB();
        b.method();
    }
}

总结:包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的

权限控制

  • **import关键字:**导入包中的类,不导入包中的包文件

    导包的原因:为了简化类名书写

  • **包的原则:**用到哪个类,就导入哪个类。

package mytest;
// packa\DemoA.class
// packa\test\DemoAA.class
// 为了解决类的繁琐我们可以用import导入
import packa.DemoA;
// 假设我们包中有很多类可以使用 * 进行通配
// 这边只会导入DemoA不会导入DemoAA
import packa.*;

import packa.test.*;
class PackTestDemo{
    
    
    public static void main(String[] args){
    
    
        DemoA d = new DemoA();
        d.show();
        
        DemoB b = new DemoB();
        b.method();
    }
}

java打包工具:jar

  • 压缩:jar -cvf haha.jar mytest
    • -c :创建新的归档文件
    • -v:输出生成详细信息
    • -f:指定归档文件名
  • 解压缩:jar -xvf haha.jar
    • -x:解压缩

想输入jar包的内容:set path = ./haha.jar java mytest.PackTestDemo

猜你喜欢

转载自blog.csdn.net/qq_38205875/article/details/107813495