面向对象----类

1、成员方法传参机制

        对于基本数据类型,传递的是值(值拷贝)

AA aa = new AA();

int x = 10;

int y = 20;

aa.swape(x,y);//输出a = 20,b = 10但是x,y的值依然不变

System.out.println("x的值:" + x +"\n"+"y的值:" + y);

AA aa = new AA();

int x = 10;

int y = 20;

aa.swape(x,y);//输出a = 20,b = 10但是x,y的值依然不变

System.out.println("x的值:" + x +"\n"+"y的值:" + y);

2、引用数据类型的传参机制

引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!

3、递归重要规则 

  1. 执行一个方法时,就创建了一个新的受保护的独立空间(栈空间)
  2. 方法的局部变量是独立的,不会相互影响,比如n变量
  3. 如果方法中使用的是引用数据类型(比如数组,对象等)就会共享该引用类型的数据
  4. 递归必须向退出递归的条件逼近,否则就是无限递归
  5. 当一个方法执行完毕,或者遇到return就会返回,遵守谁调用就把结果返回给谁,同时方法执行完毕或者返回时,该方法也就执行完毕了。

4、 方法重载和方法重写的区别

  1. 方法重载:        

                方法名必须相同

                形参列表必须不同(形参个数或顺序,至少有一样不同,参数名无要求)

                返回类型无要求

5、可变形式参数 

基本语法:

                访问修饰符 返回类型 方法名(数据类型... 形参名) {
                }
public int sum(int... nums) {
//System.out.println("接收的参数个数=" + nums.length);
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            res += nums[i];
        }
        return res;
    }
         使用可变参数时,可以当做数组来使用 即 nums 可以当做数组,遍历 nums

6、java变量作用域

 java编程中,主要的变量就是属性(成员变量)和局部变量(定义在成员方法中的变量)

  1. 全局变量:也就是属性,成员变量,作用域为整个类体
  2. 局部变量:也就是除了属性之外的变量,作用域在它的代码块中!
  3. 全局变量的属性可以不用赋值直接使用,因为有默认值。局部变量必须复制后才可以使用,没有默认值。
  4. 属性和全局变量可以重名,访问遵循就近原则,同一个作用域内不可重名
  5. 修饰符不同:全局变量/属性可以加修饰符,局部变量不可以加修饰符

7、构造器/构造方法 

  1. 构造器没有返回值,也不用写void
  2. 方法名必须和类名一致
  3. 参数列表和成员方法一样的规则,构造器调用由系统完成,主要是初始化成员变量参数不是创建对象,在创建对象时,系统自动调用该类的构造方法。当程序员调用构造器时,系统会先为该对象分配内存空间,并为这个对象执行默认初始化。当系统开始执行构造器的构造体之前,系统已经创建一个对象,只是这个对象还不能被外部程序访问,只能在该构造器中通过this来引用
  4. 为对象分配空间——>实例变量默认初始化——>执行构造器的执行体——>通过this给实例变量赋值——>构造器返回对象给引用变量
  5. 用this调用另一个重载的构造器只能在构造器中使用,而且必须作为构造器执行体的第一条语句
  6. 使用super调用父类构造器也必须出现在子类构造器执行体的第一行,所以this调用和super调用不会同时出现
  7. 子类构造器执行体中既没有super调用,也没有this调用,系统将会执行在执行子类构造器之前,隐式调用父类无参数的构造器。
  8. 一个类可以有多个构造方法,重载。程序员没有定义构造器,系统会自动给类生成一个无参构造器。一旦自己定义了构造器,默认构造器就被覆盖了,不能使用无参构造器了,除非显示的定义一下无参构造器(建议,很重要)

对象创建的流程分析 :

  1. 加载Person类的信息(Person.class),类加载阶段,只会加载一次
  2. 在堆内存中分配空间,即对象的地址----->对象创建了
  3. 完成对象的初始化--->默认初始化(属性不复制,默认值)、显式初始化(显式赋值)、构造器初始化
  4. 把对象在堆内存中的地址,返回给p(p时对象名,也可以理解为对象引用)

 Java中是构造器创建对象 “这句话是完全错误的。Java中构造器的作用主要是为了初始化变量的值...其实在执行构造器之前,Java对象所需要的内存空间,已经产生了...一般可以理解为由new关键字创建出来的哦。

        常见的两种不通过new 关键字创建对象的方式如下:

                1)通过Java的序列化和反序列化,来创建相关的对象...

                        需要注意的是在使用java的序列化和反序列化的时候要使对应的实体类实现Serializable序列化接口哦...

                2)通过Java的clone来创建相关的对象...

 8、this关键字

this关键字的意义被解释为“指向当前对象的引用”

1、this:表示自身对象,也就是本对象自己

2、this.属性名:表示本对象自己的属性

3、this.方法名:表示本对象自己的方法

4、this(参数)表示本对象自身的构造方法(注:”构造方法”这个概念是相对于”类”而言的,但具体到this(参数)这种用法时,表示”我这个对象自己的构造方法”)

5、外部类名.this.属性:表示在内部类中调用的是外部类的某个属性(调用外部类方法亦同)
 

9、访问修饰符

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
  1. 公开级别:用 public 修饰,对外公开
  2. 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
  3. 默认级别:没有修饰符号,向同一个包的类公开.
  4. 私有级别:用 private 修饰,只有类本身可以访问,不对外公开0eb2d8de30c84e5c89e571c71a39e0fd.png

 修饰符可以用来修饰类中属性、方法以及类。只有默认的和public可以修饰类

 

10、面向对象编程有三大特征:封装、继承和多态 

封装:把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在方法内部,程序的其他部分只有通过被授权的方法才能对数据进行操作

  1. 将属性私有化private(不能直接修改属性)
  2. 提供一个公共的public方法setXxx(),用于对属性的赋值
  3. 提供一个公共的public的方法getXxx(),用于对属性值的获取

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中 抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来 声明继承父类即可。子类会自动拥有父类属性和方法。

 

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访 问,要通过父类提供公共的方法去访问
  2. 非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问
  3. 子类必须调用父类的构造器, 完成父类的初始化
  4. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过。(最好显式声明无参构造器)
  5. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
  6. super 在使用时,必须放在构造器第一行( super 只能在构造器中使用)
  7. 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
  8. super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  9. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
  10. java 所有类都是 Object 类的子类, Object 是所有类的基类

super 关键字:super 代表父类的引用,用于访问父类的属性、方法、构造

                        不能访问父类私有属性和私有方法。

  1. 调用父类构造器,分工明确,父类属性由父类初始化,子类属性由子类初始化
  2. 当子类中有和父类重名的成员(属性和方法),为了访问父类成员,必须使用super。如果没有重名,使用super,this,直接访问是一样的效果。
  3. super的访问不限于直接父类,多个父类都重名,就近原则访问

 super和this的比较

d5b54af7ff714ae29dde9ec942653685.png

 

11、方法重写--override 

方法重写(方法覆盖)就是子类有一个方法和父类某个方法的名称,返回类型,参数都一样

        注意:子类方法的返回类型和父类方法返回类型一样, 或者是父类返回类型的子类

                    如父类方法返回Object,子类重写该方法可以返回任意引用类型,如String

                   子类方法不能缩小父类方法的访问权限,public > protected > 默认>private

dfc798ebdb9a4176ab16f115d1c44700.png

 29cfab2f40ae48769bd84b06f8ebae4e.png

 

12、多态 

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
多态的具体体现:
  1. 方法的多态:重写和重载就体现多态,我们传入不同的参数,就会调用不同 sum 方法,就体现多态。
  2. 对象的多态
    1. 一个对象的编译类型和运行类型可以不一致
    2. 编译类型在定义对象时就确定了,不能改变
    3. 运行类型时可以变化的
    4. 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
      Animal animal = new Dog();编译类型是Animal,运行时类型是Dog

多态细节注意:多态的前提是两个对象存在继承关系,子类重写父类的方法 

  1. 本质:父类引用指向子类的对象(向上转型)
  2. 语法:父类类型   引用名  = new  子类类型();
  3. 特点:编译类型看左边,运行类型看右边,可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有的成员(需要向下转型),最终运行效果看子类的具体实现。
  4. instanceOf 比较操作符,用于判断对象的 运行类型是否为 XX 类型或 XX 类型的子类型
​​​​                 System.out.println(str instanceof Object);//true

属性没有重写之说!属性的值看编译类型,当父类引用指向子类对象时,子类中有父类同名属性,访问的依然时父类属性,要想访问子类属性,new子类对象。

 

public class PolyDetail02 {
        public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
            Base base = new Sub();//向上转型
            System.out.println(base.count);// ? 看编译类型 10
            Sub sub = new Sub();
            System.out.println(sub.count);//? 20
        }
    }

    class Base { //父类
        int count = 10;//属性
    }

    class Sub extends Base {//子类
        int count = 20;//属性
    }

 

13、动态绑定机制 

静态绑定是指在编译期就可以明确方法调用,而动态绑定是要等到运行时才能确定。一静一动,编译期是“静”,运行时是“动”;一前一后,编译在前,运行在后。

方法定义的形参类型是父类类型,实参允许是子类类型。

14、Object类详解 

14.1、==和 equals 的对比 [面试题]

equals()方法是顶级父类Object类中的方法,用来判断引用类型是否相等,默认是判断地址是否相等,子类中往往重写该方法,用于判断内容是否相等,如String,Integer
 
                         Integer integer1 = new Integer(1000);
                        Integer integer2 = new Integer(1000);
                        System.out.println(integer1 == integer2);//false
                        System.out.println(integer1.equals(integer2));//true
 
== 是一个比较运算符,既可以判断基本类型,又可以判断引用类型
  1. 如果判断的是基本类型,判断值是否相等
  2. 如果判断引用类型,判断地址是否相等,即判断是否为一个对象。与默认的equals()方法用法一致。equals与hashcode是有契约的(无论什么时候你重写了equals方法,你同样要重写hashcode()方法)
  3. 字符串的==和equals对比:
    1. 字符串的比较是一个常见的情景,因为java.lang.String类重写了equals方法,它返回true如果两个字符串对象包含有相同的内容,但是==只有他们的引用地址相同时才返回true

总结:

使用==比较原生类型如:boolean、int、char等等,使用equals()比较对象。

==返回true如果两个引用指向相同的对象,equals()的返回结果依赖于具体业务实现

字符串的对比使用equals()代替==操作符

instanceof是用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型

                使用方法:对象 instanceof 类名

                返回值:boolean类型true 或者 false

 

int it = 65;
float fl = 65.0f;
System.out.println(“65 和 65.0f 是否相等?” + (it == fl));//T
 
char ch1 = ‘A’; char ch2 = 12;
System.out.println(“65 和‘A’是否相等?” + (it == ch1));//T
System.out.println(“12 和 ch2 是否相等?” + (12 == ch2));//T
 
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println("str1 和 str2 是否相等?"+ (str1 == str2)); //F
System.out.println(“str1 是否 equals str2?”+(str1.equals(str2)));//T
 
System.out.println(“hello” == new java.sql.Date()); //编译错误

14.2、 hashCode 方法

  1. 提高具有哈希结构的容器的效率!
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址。

hashCode()和equals()的关系

  1. 只要重写 equals,就必须重写 hashCode
    1. HashSet、HashMap底层在添加元素时,会先判断对象的hashCode是否相等,如果hashCode相等才会用equals()方法比较是否相等。换句话说,HashSet和HashMap在判断两个元素是否相等时,会先判断hashCode,如果两个对象的hashCode不同则必定不相等
  2. 复习集合以及数据结构的时候再重新整理hash

 14.3、 toString 方法

默认返回:全类名+@+哈希值的十六进制,子类往往重写 toString 方法,用于返回对象的属性信息。
当直接输出一个对象时,toString 方法会被默认的调用, 比如 System.out.println(monster); 就会默认调用
monster.toString()

15、类变量和类方法 static

类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问 ,在类加载时就初始化了,也就是说你没有创建对象,只要类加载了就可以使用。

静态方法只能访问静态成员,普通成员方法,既可以访问 非静态成员,也可以访问静态成员。

在 static 方法中,不可以使用 this 关键字

public static int getTotalPerson() {
        id++;// 错误, id是成员变量没用static修饰,不能在静态方法中访问
        return total;
}

结论:static方法不能被重写! 

public class Main {
    public static void main(String[] args) {
        A a = new A();
        a.test();// 输出 A=>test()

        B b = new A();
        b.test();// 输出 B=>test(),说明父类中的test()方法没有被重写
    }
}

// 父类
class B {
    public static void test(){
        System.out.println("B=>test()");
    }
}

// 子类
class A extends B {
    public static void test(){
        System.out.println("A=>test()");
    }
}

运行结果:父类中的test()方法没有被重写!

在Java中,如果子类和父类中各有一个static方法,且它们的返回值类型、方法名、参数列表都相同(具有相同的签名),那么这两个static方法并不具有重写关系。

  1. 静态方法不具有多态性。子类可以继承父类中的静态方法,但是不能重写父类中的静态方法。
  2. 重写的前提:有继承关系,子类重新编写父类的方法(注意不重写属性)
  3. 子、父类中两个方法的方法名、参数列表必须相同,方法体不同。
  4. 重写注意事项:
  5. 子类中重写后方法的访问修饰符 不能小于 父类方法的访问修饰符(public > protected > default > private)
  6. 子类方法的返回值以及抛出的异常要 小于等于 父类方法。比如:父类中throws IOException,子类中不能throws Exception,因为Exception > IOException
  7. 若父类方法为private,则子类对其重写无效。
  8. 父类方法用final修饰也不能被重写

因此不能用static、private、final修饰抽象方法。

16、代码块 

代码块又称作初始化块,述域类中的成员,是类的一部分,直接由{}包围起来,没有方法名,没有返回值,没有参数,只有方法体,在类加载阶段或创建对象时隐式调用。

  1. 静态代码块
    1. static{}
    2. 对类进行初始化,随着类加载而执行,而且只会执行一次
  2. 非静态代码块
    1. {}
    2. 每创建一个对象就会执行一次
  3. 相当于另一种形式的构造器,对构造器的补充机制,做类加载时的初始化操作。
  4. 创建对象,都会先调用代码块的内容
  5. 类什么时候被加载?
    1. 创建对象实例时(new)
    2. 创建子类对象实例,父类也会被加载
    3. 使用静态成员(静态属性,静态方法)
  6. 创建一个对象时,在一个类中调用顺序是:
    1. 调用静态代码块和静态属性初始化(优先级一样,同时进行)如果有多个就按他们定义的顺序进行
    2. 调用普通代码块和普通属性的初始化(优先级一样,同时进行)如果有多个就按他们定义的顺序进行
    3. 调用构造方法
  7. 构造器最前面其实隐含了super()和调用普通代码块,静态代码块,静态属性初始化在类加载的时候就执行完毕了,因此优先于构造器和普通代码块执行的
  8. 创建子类对象时,静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法调用顺序:
    1. 父类的静态代码块和父类的静态属性初始化先执行
    2. 子类的静态代码块和子类的静态属性初始化执行
    3. 父类的普通代码块和普通属性初始化执行
    4. 父类的构造方法执行
    5. 子类的普通代块和普通属性初始化执行
    6. 子类的构造方法执行
 

17、单例设计模式 --静态属性和静态方法的经典应用

单例模式:采取一定的方法保证整个软件系统中,对某个类只能存在一个对象实例,并且该类只能提供一个取得其对象的实例方法

分类

  1. 饿汉式-------定义static对象并初始化,private构造器
    1.  将构造器私有化
    2.  
      在类的内部直接创建对象(该对象是 static),定义一个static静态属性对象,且使用构造器初始化
    3.  
      在类的内部直接创建对象(该对象是 static)
    4. private static GirlFriend gf = new GirlFriend("小红红");//初始化了一个对象
      //如何保障我们只能创建一个 GirlFriend 对象
      //步骤[单例模式-饿汉式]
      //1. 将构造器私有化
      //2. 在类的内部直接创建对象(该对象是 static)
      //3. 提供一个公共的 static 方法,返回 gf 对象
      private GirlFriend(String name) {
      System.out.println("構造器被調用.");
      this.name = name;
      }
      public static GirlFriend getInstance() {
      return gf;
      }

       

  2. 懒汉式-------定义static对象未初始化,默认null,private构造器
    1. 仍然构造器私有化
    2.  
      定义一个static静态属性对象,默认不初始化为null
    3.  
      提供一個 public 的 static 方法,先判断对象是否为null,为null说明第一次创建,不为null,直接返回这个对象。可以返回这个静态对象
    4.  
      懒汉式,只有当用户使用 getInstance 时,才返回 对象, 后面再次调用时,会返回上次创建的静态对象。
      private String name;
      public static int n1 = 999;
      private static Cat cat ; //默认是 null,未初始化
      //步驟
      //1.仍然構造器私有化
      //2.定義一個 static 靜態屬性對象
      //3.提供一個 public 的 static 方法,可以返回一個 Cat 對象
      //4.懶漢式,只有當用戶使用 getInstance 時,才返回 cat 對象, 後面再次調用時,會返回上次創建的 cat 對象
      // 從而保證了單例
      private Cat(String name) {
      System.out.println("構造器調用...");
      this.name = name;
      }
      public static Cat getInstance() {
      if(cat == null) {//如果還沒有創建 cat 對象
      cat = new Cat("小可愛");
      }
      return cat;
      }
    5. 57ed1c2fa18245dab5d60b38779a297b.png

 18、final 关键字

  1. final可以修饰类、属性、方法和局部变量
  2. 使用final的时机:
    1. 当不希望类被继承时,可以用final修饰类
    2. 当不希望父类的某个方法被子类重写时,可以用final关键字
    3. 当不希望类的某个属性被修改时,可以用final修饰为一个常量
    4. 当不希望某个局部变量被修改时,可以使用final修饰
  3. 使用细节
    1. final修饰的属性在定义时,必须赋值,并且不可修改,可以在静态代码块中、构造器中或者定义时赋值
    2. 如果final修饰的属性为静态的,则初始化位置只能是定义时、静态代码块中,不能在构造器中赋值
    3. final修饰的类不能被继承,但是可以实例化对象
    4. 如果类不是final类,但是含有final修饰的方法,则该方法不能重写,但可以被继承
    5. 一般来说如果类是final的就不用再把方法修饰成final了
    6. final不能修饰构造方法
    7. final往往和static搭配使用,效率更高,不会导致类加载
    8. 包装类(Integer、Boolean、Double、Float等都是final类),String也是final类

19、抽象类 和抽象方法

当父类某些方法需要声明,但又不确定如何实现时,可以将其声名为抽象方法,那么这个类就是抽象类。abstract修饰

所谓没有实现就是指,没有方法体, 当一个类中存在抽象方法时,需要将该类声明为 abstract 类。一般来说,抽象类会被继承,有其子类来实现抽象方法.

  1. 用abstract关键字修饰一个类,就是抽象类
    1. 访问修饰符  abstract  类名{}
  2. 用abstract关键字修饰的方法就是抽象方法
    1. 访问修饰符  abstract  返回值类型  方法名(参数列表);//没有方法体
  3. 抽象类的价值更多在于模式架构设计,让子类继承并实现抽象类

抽象类注意事项:

  1. 抽象类不能被实例化
  2. 抽象类不一定要包含abstract方法
  3. 一旦类包含了abstract方法,则这个类必须声明为abstract
  4. abstract只能修饰类和方法,不能修饰属性和其他的
  5. 抽象类可以由任意成员,和其他类一样
  6. 抽象方法不能有主体,即不能实现abstract修饰的抽象方法
    1. 访问修饰符  abstract  返回值类型  方法名(参数列表);
  7. 如果一个类继承了抽象类,则它必须要实现抽象类中的所有抽象方法,除非它自己也声明为抽象类
  8. 抽象方法不能使用private、final和static修饰,因为这些关键字都是和方法重写违背的。

20、模板设计模式---抽象类的经典实践

抽象类体现的就是一种模板设计模式,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式

  1. 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现
  2. 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。

在抽象父类中调用抽象方法,完成方法增强,子类实现该方法,并调用父类方法,利用多态oop思想实现子类方法增强

abstract public class Template { //抽象类-模板设计模式

    public abstract void job();//抽象方法

    public void calculateTime() {//实现方法,调用job方法
        //得到开始的时间
        long start = System.currentTimeMillis();
        job(); //动态绑定机制
        //得的结束的时间
        long end = System.currentTimeMillis();
        System.out.println("任务执行时间 " + (end - start));
    }
}
public class AA extends Template {

    //计算任务
    //1+....+ 800000
    @Override
    public void job() { //实现Template的抽象方法job

        long num = 0;
        for (long i = 1; i <= 800000; i++) {
            num += i;
        }
    }
}
public class TestTemplate {
    public static void main(String[] args) {

        AA aa = new AA();
        aa.calculateTime(); //这里还是需要有良好的OOP基础,对多态

        BB bb = new BB();
        bb.calculateTime();
    }
}

21、接口 

接口就是一套规范,给出一些没有实现的方法,封装在一起,到某个类使用时,在根据具体情况把这些方法实现。

语法:

interface 接口名{
    //属性
    //抽象方法
     
}

class 类名 implements 接口{
    //自己的属性
    //自己的方法
    //必须实现的接口中的抽象方法
}

接口时更加抽象的抽象类,抽象类里可以有非抽象方法写具体的有方法体,但是接口中所有的方法都时抽象方法,jdk8之后,可以是静态方法,也可以是用default修饰的默认方法可以有方法体,不用被实现类实现。

default String setTime(String newTime) {
        return "time set to " + newTime;
    }

jdk8之后,接口中可以有静态方法,也可以用default修饰的默认方法,也就是说接口中可以有方法的具体实现了。默认方法在实现类中可以直接使用

注意:如果一个类实现了多个接口,多个接口中有同名默认方法,则在实现类中必须重写该默认方法。

注意: 

  1. 接口不能被实例化
  2. 接口中所有方法都是public方法,接口中的抽象方法,可以不用abstract修饰 void a();
    1. interface中的方法自动就是public的,即使不显式的声明。
    2. 如果实现类implementation和接口interface不在一个包package下面,则无法实现类无法实现除了public之外的其他访问权限。
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现
  4. 抽象类实现接口,可以不用实现接口的方法
  5. 一个类可以同时实现多个接口
  6. 接口中的属性,只能是final的,而且是public、static、final修饰符。比如int a = 1;实际上是public static final int a = 1,必须初始化
  7. 接口中的属性访问方式:接口名.属性名
  8. 接口不能继承其他的类,但是可以继承多个别的接口
  9. 接口的修饰符只能是public和默认,这点和类的修饰符一致

实现接口和继承类 

  1. 实现机制:是对单继承的一种补充
    1. 例如一只小猴子,他爸爸是只大猴子,小猴子可以继承大猴子的一些特性和特征,这就是继承的特性。再引入鱼、鸟、兔子、猫等动物,小猴子就无法通过继承来获取他们身上的特性和特征了,这就引入了接口,通过接口小猴子就可以获取他们身上的特征了,也就是对小猴子的一个扩展,这样,小猴子成功的学会了游泳。
  2. 当子类继承了父类,就自动拥有了父类的功能
    1. 子类不能继承父类的构造器:子类有自己的构造器
    2. 子类是可以继承父类的私有成员的,只是不能直接访问而己。
    3. 子类是不能继承父类的静态成员的。子类只是可以访问父类的静态成员,父类的静态成员只有一份可以被子类共享访问,共享并非继承。
      1. 子类是不继承父类的static变量和方法的。因为这是属于类本身的。但是子类是可以访问的。
        子类和父类中同名的static变量和方法都是相互独立的,并不存在任何的重写的关系。
  3. 如果子类需要扩展功能,可以通过实现接口的方式扩展
  4. 可以理解为java接口是单继承的补充

接口的多态特性 

  1. 接口名  变量名   =  new  实现类名();接口类型的变量可以指向,实现了该接口的类的对象实例
  2. public void m(接口名  变量名){},形参可以传实现类的实例对象,体现多态
  3. 接口可以传递,A接口继承B接口,,C类实现A接口同时也实现B接口
  4. //多态数组 -> 接口类型数组
    Usb[] usbs = new Usb[2];
    usbs[0] = new Phone_();
    usbs[1] = new Camera_();
    /*
    给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(),
    请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外,
    还需要调用 Phone 特有方法 call
    */
    for(int i = 0; i < usbs.length; i++) {
        usbs[i].work();//动态绑定.. 
        //和前面一样,我们仍然需要进行类型的向下转型
        if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
            ((Phone_) usbs[i]).call();
        }
    }
    interface Usb{
    void work();
    }
    class Phone_ implements Usb {
        public void call() {
            System.out.println("手机可以打电话...");
        }
        @Override
        public void work() {
            System.out.println("手机工作中...");
        }
    }

22、内部类 

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套内部类的类称为外部类。

        类的五大成员:属性、方法、构造器、代码块、内部类

内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

c254091c2a1d41e7b639c97d876c74f6.png

 

内部类分类:

  1. 定义在类中的局部位置(方法体或者代码块中)
    1. 局部内部类
      1. 局部内部类定义在外部类的局部位置,如方法体,并且有类名
      2. 可以直接访问外部类的所有成员,包含私有的,直接访问
      3. 不能添加访问修饰符,因为它本身地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以用final修饰
      4. 作用域:仅仅在定义它的方法或者代码块中
      5. 外部类访问内部类成员,需要创建内部类对象再访问。在定义局部内部类的方法体中创建对象
      6. 外部其他类不能访问局部内部类
      7. 如果外部类和局部类的成员重名,默认就近原则,如果此时想访问外部类成员可以使用(外部类名.this.成员)去访问
    2. 匿名内部类-----没有类名(重要)
      1. 本质是类,该类没有名字,同时还是一个对象
      2. 匿名内部类是定义在外部类的局部位置,比如方法体,以形参形式创建。
      3. new 类或者接口(参数列表){类体}
      4. 实现接口的匿名内部类,没有类名,直接实现并创建对象。可以直接使用匿名内部类实现接口,系统底层自动创建实现类

        1. //实现接口的匿名类:
          
          
          interface IA {//接口
              public void cry();
          }
          class Outer04{
          //jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了Outer04$1实例(运行类型),并且把地址
          //返回给 tiger引用
          //匿名内部类使用一次,就不能再使用
          IA tiger = new IA() {
                      @Override
                      public void cry() {
                          System.out.println("老虎叫唤...");
                      }
                  };
          }
          
      5. 匿名子类(继承父类):

        1. class Animal {
                  private String name;
          
                  public Animal(String name) {
                      this.name = name;
                  }
          
                  public void printAnimalName() {
                      System.out.println(bird.name);
                  }
              }
          
          // 鸟类,匿名子类,继承自Animal类,可以覆写父类方法
              Animal bird = new Animal("布谷鸟") {
          
                  @Override
                  public void printAnimalName() {
                      accessTest(); // 访问外部类成员
                      System.out.println(ANIMAL);  // 访问外部类final修饰的变量
                      super.printAnimalName();
                  }
              };

           

        2. 操作符:new;
        3. 一个要实现的接口或要继承的类,案例一中的匿名类实现了HellowWorld接口,案例二中的匿名内部类继承了Animal父类;
        4. 一对括号,如果是匿名子类,与实例化普通类的语法类似,如果有构造参数,要带上构造参数;如果是实现一个接口,只需要一对空括号即可;
        5. 一段被"{}"括起来类声明主体;
        6. 末尾的";"号(因为匿名类的声明是一个表达式,是语句的一部分,因此要以分号结尾)。
      6. 匿名内部类既是一个类的定义同时它本身又是一个对象,既有定义类的特征又有创建对象的特征。

      7. 可以直接访问外部类的所有成员,包含私有的

      8. 不能添加访问修饰符,因为它的地位就是一个局部变量

  2. 定义在类的成员位置
    1. 成员内部类
    2. 静态内部类
  3. 2a4fa42bccf546de8fad597c34277110.png

23、匿名内部类的最佳实践 ----当实参直接传递

//接口
interface IL {
    void show();
}

class Picture implements IL {

    @Override
    public void show() {
        System.out.println("这是一副名画XX...");
    }
}


//静态方法,形参是接口类型
    public static void f1(IL il) {
        il.show();
    }

//当做实参直接传递,简洁高效
        f1(new IL() {
            @Override
            public void show() {
                System.out.println("这是一副名画~~...");
            }
        });


//传统方法
        f1(new Picture());//动态绑定,接口引用指向实现类对象

24、枚举类

  1. 对于季节而已,他的对象(具体值),是固定的四个,不会有更多
  2. 枚: 一个一个 举: 例举 , 即把具体的对象一个一个例举出来的类,就称为枚举类
  3. 创建 Season 对象有如下特点:
    1. 季节的值是有限的几个值(spring, summer, autumn, winter)
    2. 只读,不需要修改。
  4. 枚举对应英文(enumeration, 简写 enum),枚举是一组常量的集合。枚举属于一种特殊的类,里面只包含一组有限的特定的对象
  5. 枚举的二种实现方式
    1. 自定义类实现枚举
      1. 不需要提供setXxx方法,因为枚举对象值通常为只读
      2. 对枚举对象/属性使用final+static共同修饰,实现底层优化
      3. 枚举对象名通常使用全部大写,常量命名规范
      4. 枚举对象根据需要,也可以有多个属性
      5. public class Enumeration02 {
            public static void main(String[] args) {
                System.out.println(Season.AUTUMN);
                System.out.println(Season.SPRING);
            }
        }
        
        class Season {//类
            private String name;
            private String desc;//描述
        
            //定义了四个对象, 固定.
            public static final Season SPRING = new Season("春天", "温暖");
            public static final Season WINTER = new Season("冬天", "寒冷");
            public static final Season AUTUMN = new Season("秋天", "凉爽");
            public static final Season SUMMER = new Season("夏天", "炎热");
        
        
            //1. 将构造器私有化,目的防止 直接 new
            //2. 去掉setXxx方法, 防止属性被修改
            //3. 在Season 内部,直接创建固定的对象
            //4. 优化,可以加入 final 修饰符
            private Season(String name, String desc) {
                this.name = name;
                this.desc = desc;
            }
        
            public String getName() {
                return name;
            }
        
            public String getDesc() {
                return desc;
            }
        }
        • 构造器私有化
        • 本类内部创建一组对象[四个 春夏秋冬]
        • 可以提供 get 方法,但是不要提供 set
        • 对外暴露对象(通过为对象添加 public final static 修饰符)
    2. 使用 enum 关键字实现枚举
      public class Enumeration03 {
          public static void main(String[] args) {
              System.out.println(Season2.AUTUMN);
              System.out.println(Season2.SUMMER);
          }
      }
      
      enum  Season2 {//类
      
          //定义了四个对象, 固定.
      //    public static final Season SPRING = new Season("春天", "温暖");
      //    public static final Season WINTER = new Season("冬天", "寒冷");
      //    public static final Season AUTUMN = new Season("秋天", "凉爽");
      //    public static final Season SUMMER = new Season("夏天", "炎热");
          //如果使用了enum 来实现枚举类
          //1. 使用关键字 enum 替代 class
          //2. public static final Season SPRING = new Season("春天", "温暖") 直接使用
          //   SPRING("春天", "温暖") 解读 常量名(实参列表)
          //3. 如果有多个常量(对象), 使用 ,号间隔即可
          //4. 如果使用enum 来实现枚举,要求将定义常量对象,写在前面
          //5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()
          SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"),
          SUMMER("夏天", "炎热")/*, What()*/;
      
          private String name;
          private String desc;//描述
      
          private Season2() {//无参构造器
      
          }
      
          private Season2(String name, String desc) {
              this.name = name;
              this.desc = desc;
          }
      
          public String getName() {
              return name;
          }
      
          public String getDesc() {
              return desc;
          }
      }
      1. 如果使用了enum 来实现枚举类
      2. 使用关键字 enum 替代 class
      3. public static final Season SPRING = new Season("春天", "温暖") 直接使用:SPRING("春天", "温暖") 解读 常量名(实参列表)
      4. 如果有多个常量(对象), 使用 ,号间隔即可
      5. 如果使用enum 来实现枚举,要求将定义常量对象,写在前面
      6. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()
 
                           
表 1 Enum类的常用方法
方法名称描述
values()以数组形式返回枚举类型的所有成员
valueOf()将普通字符串转换为枚举实例
compareTo()比较两个枚举成员在定义时的顺序
ordinal()获取枚举成员的索引位置

                                      Season2.values()-----返回枚举类中所有元素{SPRING,WINTER,AUTUMN,SUMMER}

 

25、注解 

注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
三个基本的 Annotation:
  1. @Override: 限定某个方法,是重写父类方法, 该注解只能用于方法
  2. @SuppressWarnings: 抑制编译器警告
    1. ,@SuppressWarnings({"rawtypes", "unchecked", "unused"}),在{""} 中,可以写入你希望抑制(不显示)警告信息
      1. all,抑制所有警告
      2. boxing,抑制与封装/拆装作业相关的警告
      3. cast,抑制与强制转型作业相关的警告
      4. finally,抑制与未传回 finally 区块相关的警告
      5. fallthrough,抑制与 switch 陈述式中遗漏 break 相关的警告
  3. @Deprecated: 用于表示某个程序元素(类, 方法等)已过时
元注解的种类
  1. Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
    1. 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留多长时间
    2. @Retention 的三种值 
      1. RetentionPolicy.SOURCE: 编译器使用后,直接丢弃这种策略的注释
      2.  
        RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认
      3. RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以
        通过反射获取该注解

26、java异常处理 

26.1、异常介绍

执行过程发生的异常事件分为两大类:

  1. Error(错误):Java虚拟机无法解决的严重问题,如JVM系统内部错误,资源耗尽等。比如栈溢出、OOM,程序会崩溃。
  2. Exception:其他因变成错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如,空指针访问、视图读取不存在的文件、网络中断等。Exception分为两大类:
    1. 运行时异常
      1. 编译器一般检查不出,一般是编程时的逻辑错误,是程序员应该避免出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
      2. 对于运行时异常,可以不做处理,因为这类异常很普遍,若全处理可能对程序的可读性和循行效率产生影响。
      3. 常见的运行时异常:
        1. NullPointerException 空指针异常
        2. ArithmeticException 数学运算异常
        3. ArrayIndexOutOfBoundsException 数组下标越界异常
        4. ClassCastException 类型转换异常
        5. NumberFormatException 数字格式不正确异常[]
    2. 编译时异常-----主要处理的异常
      1. 是编译器必须处理的异常,否则代码不能编译通过
      2. 常见的编译异常
        1. SQLException数据库操作异常,如获取连接,可能没有
        2. IOException操作文件时发生的异常,哟可能文件不存在
        3. FileNotFoundException操作一个不存在的文件发生的异常
        4. ClassNotFoundException加载类,而类不存在发生的异常
  3. f1bc7883eba548a995cad36b1a140cd5.png

26.2、异常处理机制 

异常出现时,对异常处理的机制

异常处理的方式

  1. try-catch-finally

     

    1. 程序员在代码中捕获发生的异常,自行处理
    2. 05412520fbdb4c90b8fc39ad2424ab78.png
    3. try块用于包含可能出错的代码,catch块用于处理try块里发生的异常
    4. 如果try块中异常发生了,则异常发生后面的代码不会执行,直接进入到catch块中,如果异常没发生,则顺序执行try的代码块,不会进入catch
    5. 如果希望不管是否发生异常,都执行某段代码,则用finally{}
    6. 可以有多个catch语句,捕获不同的异常,要求父类异常在后,子类异常在前,如果发生异常,只会匹配一个catch
    7. catch住编译时异常,不会影响try-catch后面代码执行
  2. throws

     

    1. 将发生的异常抛出,交给调用者(方法)处理,最顶级的处理者就是JVM
    2. 27d6cd48a54547f8b78afee4ee414651.png
    3. 如果一个方法中语句执行时可能生成某种异常,但是并不能确定是如何处理这种异常,此方法应显示的声明抛出异常,表明该方法不对这些异常进行处理,而由该方法的调用者负责处理
    4. 在方法声明中使用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。如最大的Exception
  3. 注意:
    1. 对于编译异常,程序中必须处理,比如try-catch或者throws
    2. 对于运行时异常,程序中如果没有处理,默认就是throwa处理
    3. 子类重写父类方法时,对于异常抛出的规定:子类重写父类方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出异常的子类型。
    4. 在throws过程中,如果有方法try-catch,相当于处理异常,就可不必throws

26.3、自定义异常

自定义异常:

  1. 定义类:自定义异常类名继承Exception或RuntimeException
  2. 如果继承Exception就属于编译异常
  3. 如果继承RuntimeException,属于运行时异常(一般来说继承RuntimeException)
  4. throw 和 throws 的区别
    1. fa80e38bf5e2407ca59a24eb0f70708d.png

       

public class CustomException {
    public static void main(String[] args) /*throws AgeException*/ {

        int age = 180;
        //要求范围在 18 – 120 之间,否则抛出一个自定义异常
        if(!(age >= 18 && age <= 120)) {
            //这里我们可以通过构造器,设置信息
            throw new AgeException("年龄需要在 18~120之间");
        }
        System.out.println("你的年龄范围正确.");
    }
}
//自定义一个异常
//老韩解读
//1. 一般情况下,我们自定义异常是继承 RuntimeException
//2. 即把自定义异常做成 运行时异常,好处时,我们可以使用默认的处理机制
//3. 即比较方便

class AgeException extends RuntimeException {
    public AgeException(String message) {//构造器
        super(message);
    }
}

自定义异常步骤:

  1. 编写一个类继承Exception或者RuntimeException
  2. 提供两个构造方法,一个无参构造,一个带有String message参数的
class AgeException extends RuntimeException {
    public AgeException(String message) {//构造器
        super(message);
    }
}

 

26.4、异常常用方法 

1、e.getMessage()------获取异常简单信息

2、e.printStackTrace()-----打印异常追踪的堆栈信息

        出异常可以看出哪些原因,从上往下看,从自己写的程序开始主要第一行,跳过sun公司程序

        java.io.FileNotFoundException: \\sss (指定的路径无效。)
                    at java.io.FileInputStream.open0(Native Method)
                    at java.io.FileInputStream.open(FileInputStream.java:195)
                    at java.io.FileInputStream.<init>(FileInputStream.java:138)
                    at java.io.FileInputStream.<init>(FileInputStream.java:93)
                    //我们自己写的包下的程序:17行

                   at  com.usst.exercise.exception_.ExceptionTest01.main(ExceptionTest01.java:17)                      

26.5、异常与方法覆盖 

子类重写的方法,不能比父类抛出更多异常或更宽泛异常,可以更少,且为父类异常及其子类:父类方法抛异常,子类可以不抛,也可以抛相同异常或者子类异常

        父类不抛,子类重写不能抛

注意:一般父类方法抛什么异常,子类重写该方法就抛什么异常就行。直接拷贝父类方法定义样式,重写方法体,即可。

        

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值