零基础学java(7)——面向对象的高级特性

第七章 面向对象的高级特性


修饰符的学习围绕三个问题:

(1)单词的意思

(2)可以修饰什么?

(3)用它修饰后有什么不同?

7.1 关键字:final

final:最终的

用法:

(1)修饰类(包括外部类、内部类类)

表示这个类不能被继承,没有子类

(2)修饰方法

表示这个方法不能被重写

(3)修饰变量(成员变量(类变量、实例变量),局部变量)

表示这个变量的值不能被修改

注意:如果某个成员变量用final修饰后,也得手动赋值,而且这个值一旦赋完,就不能修改了,即没有set方法

7.2 关键字:native

native:本地的,原生的
用法:

​ 只能修饰方法

​ 表示这个方法的方法体代码不是用Java语言实现的。

​ 但是对于Java程序员来说,可以当做Java的方法一样去正常调用它,或者子类重写它。

JVM内存的管理:

1555119319865

方法区:类的信息、常量、静态变量、动态编译生成的字节码信息

虚拟机栈:Java语言实现的方法的局部变量

本地方法栈:非Java语言实现的方法的局部变量,即native方法执行时的内存区域

堆:new出来的对象

程序计数器:记录每一个线程目前执行到哪一句指令

7.3 关键字:static

static:静态的

用法:

1、成员方法:我们一般称为静态方法或类方法

(1)不能被重写

(2)被使用

本类中:其他方法中可以直接使用它

其他类中:可以使用“类名.方法"进行调用,也可以使用"对象名.方法",推荐使用“类名.方法"

(3)在静态方法中,我们不能出现:this,super,非静态的成员

2、成员变量:我们一般称为静态变量或类变量

(1)静态变量的值是该类所有对象共享的

(2)静态变量存储在方法区

(3)静态变量对应的get/set也是静态的

(4)静态变量与局部变量同名时,就可以使用“类名.静态变量"进行区分

3、内部类:后面讲

4、代码块:静态代码块

7.4 静态代码块

1、语法格式:

【修饰符】 class 类名{
    
    
    static{
    
    
        静态代码块;
    }
}

2、作用:

协助完成类初始化,可以为类变量赋值。

3、类初始化()

类的初始化有:

①静态变量的显式赋值代码

②静态代码块中代码

其中①和②按顺序执行

注意:类初始化方法,一个类只有一个

4、类的初始化的执行特点:

(1)每一个类的()只执行一次

(2)如果一个子类在初始化时,发现父类也没有初始化,会先初始化父类

(3)如果既要类初始化又要实例化初始化,那么一定是先完成类初始化的

7.5 变量的分类与区别

1、变量按照数据类型分:

(1)基本数据类型的变量,里面存储数据值

(2)引用数据类型的变量,里面存储对象的地址值

int a = 10;//a中存储的是数据值

Student stu = new Student();//stu存储的是对象的地址值
int[] arr = new int[5];//arr存储的是数组对象的地址值
String str = "hello";//str存储的是"hello"对象的地址值

2、变量按照声明的位置不同:

(1)成员变量

(2)局部变量

3、成员变量与局部变量的区别

(1)声明的位置不同

成员变量:类中方法外

局部变量:(1)方法的()中,即形参(2)方法体的{}的局部变量(3)代码块{}中

(2)存储的位置不同

成员变量:

​ 如果是静态变量(类变量),在方法区中

​ 如果是非静态的变量(实例变量),在堆中

局部变量:栈

(3)修饰符不同

成员变量:4种权限修饰符、static、final。。。。

局部变量:只有final

(4)生命周期

成员变量:

​ 如果是静态变量(类变量),和类相同

​ 如果是非静态的变量(实例变量),和所属的对象相同,每一个对象是独立

局部变量:每次执行都是新的

(5)作用域

成员变量:

​ 如果是静态变量(类变量),在本类中随便用,在其他类中使用“类名.静态变量"

​ 如果是非静态的变量(实例变量),在本类中只能在非静态成员中使用,在其他类中使用“对象名.非静态的变量"

局部变量:有作用域

7.7 根父类

1、java.lang.Object类是类层次结构的根父类。包括数组对象。

(1)Object类中声明的所有的方法都会被继承到子类中,那么即所有的对象,都拥有Object类中的方法

(2)每一个对象的创建,最终都会调用到Object实例初始化方法()

(3)Object类型变量、形参、数组,可以存储任意类型的对象

2、Object类的常用方法

(1)public String toString():

①默认情况下,返回的是“对象的运行时类型 @ 对象的hashCode值的十六进制形式"

②通常是建议重写,如果在eclipse中,可以用Alt +Shift + S–>Generate toString()

③如果我们直接System.out.println(对象),默认会自动调用这个对象的toString()

(2)public final Class<?> getClass():获取对象的运行时类型

(3)protected void finalize():当对象被GC确定为要被回收的垃圾,在回收之前由GC帮你调用这个方法。而且这个方法只会被调用一次。子类可以选择重写。

(4)public int hashCode():返回每个对象的hash值。

规定:①如果两个对象的hash值是不同的,那么这两个对象一定不相等;

​ ②如果两个对象的hash值是相同的,那么这两个对象不一定相等。

主要用于后面当对象存储到哈希表等容中时,为了提高性能用的。

(5)public boolean equals(Object obj):用于判断当前对象this与指定对象obj是否“相等”

①默认情况下,equals方法的实现等价于与“==”,比较的是对象的地址值

②我们可以选择重写,重写有些要求:

A:如果重写equals,那么一定要一起重写hashCode()方法,因为规定:

​ a:如果两个对象调用equals返回true,那么要求这两个对象的hashCode值一定是相等的;

​ b:如果两个对象的hashCode值不同的,那么要求这个两个对象调用equals方法一定是false;

​ c:如果两个对象的hashCode值相同的,那么这个两个对象调用equals可能是true,也可能是false

B:如果重写equals,那么一定要遵循如下几个原则:

​ a:自反性:x.equals(x)返回true

​ b:传递性:x.equals(y)为true, y.equals(z)为true,然后x.equals(z)也应该为true

​ c:一致性:只要参与equals比较的属性值没有修改,那么无论何时调用结果应该一致

​ d:对称性:x.equals(y)与y.equals(x)结果应该一样

​ e:非空对象与null的equals一定是false

7.8 关键字:abstract

1、什么时候会用到抽象方法和抽象类?

当声明父类的时候,在父类中某些方法的方法体的实现不能确定,只能由子类决定。但是父类中又要体现子类的共同的特征,即它要包含这个方法,为了统一管理各种子类的对象,即为了多态的应用。

那么此时,就可以选择把这样的方法声明为抽象方法。如果一个类包含了抽象方法,那么这个类就必须是个抽象类。

2、抽象类的语法格式

【权限修饰符】 abstract class 类名{
    
    
    
}
【权限修饰符】 abstract class 类名 extends 父类{
    
    
    
}

3、抽象方法的语法格式

【其他修饰符】 abstract 返回值类型  方法名(【形参列表】);

抽象方法没有方法体

4、抽象类的特点

(1)抽象类不能直接实例化,即不能直接new对象

(2)抽象类就是用来被继承的,那么子类继承了抽象类后,必须重写所有的抽象方法,否则这个子类也得是抽象类

(3)抽象类也有构造器,这个构造的作用不是创建抽象类自己的对象用的,给子类在实例化过程中调用;

(4)抽象类也可以没有抽象方法,那么目的是不让你创建对象,让你创建它子类的对象

(5)抽象类的变量与它子类的对象也构成多态引用

5、不能和abstract一起使用的修饰符?

(1)final:和final不能一起修饰方法和类

(2)static:和static不能一起修饰方法

(3)native:和native不能一起修饰方法

(4)private:和private不能一起修饰方法

7.9 接口

1、接口的概念

接口是一种标准。注意关注行为标准(即方法)。

面向对象的开发原则中有一条:面向接口编程。

2、接口的声明格式

【修饰符】 interface 接口名{
    
    
    接口的成员列表;
}

3、类实现接口的格式

【修饰符】 class 实现类  implements 父接口们{
    
    
    
}

【修饰符】 class 实现类 extends 父类 implements 父接口们{
    
    
    
}

4、接口继承接口的格式

【修饰符】 interface 接口名 extends 父接口们{
    
    
    接口的成员列表;
}

5、接口的特点

(1)接口不能直接实例化,即不能直接new对象

(2)只能创建接的实现类对象,那么接口与它的实现类对象之间可以构成多态引用。

(3)实现类在实现接口时,必须重写所有抽象的方法,否则这个实现类也得是抽象类。

(4)Java规定类与类之间,只能是单继承,但是Java的类与接口之间是多实现的关系,即一个类可以同时实现多个接口

(5)Java还支持接口与接口之间的多继承。

6、接口的成员

JDK1.8之前:

(1)全局的静态的常量:public static final,这些修饰符可以省略

(2)公共的抽象方法:public abstract,这些修饰符也可以省略

JDK1.8之后:

(3)公共的静态的方法:public static ,这个就不能省略了

(4)公共的默认的方法:public default,这个就不能省略了

7、默认方法冲突问题

(1) 当一个实现类同时实现了两个或多个接口,这个多个接口的默认方法的签名相同。

解决方案:

方案一:选择保留其中一个

接口名.super.方法名(【实参列表】);

方案二:完全重写

(2)当一个实现类同时继承父类,又实现接口,父类中有一个方法与接口的默认方法签名相同

解决方案:

方案一:默认方案,保留父类的

方案二:选择保留接口的

接口名.super.方法名(【实参列表】);

方案三:完全重写

8、示例代码

public interface Flyable{
    
    
	long MAX_SPEED = 7900000;
    void fly();
}
public class Bird implements Flyable{
    
    
    public void fly(){
    
    
        //....
    }
}

9、常用的接口

(1)java.lang.Comparable接口:自然排序

​ 抽象方法:int compareTo(Object obj)

(2)java.util.Comparator接口:定制排序

​ 抽象方法:int compare(Object obj1 ,Object obj2)

(3)示例代码

如果员工类型,默认顺序,自然顺序是按照编号升序排列,那么就实现Comparable接口

class Employee implements Comparable{
    
    
    private int id;
    private String name;
    private double salary;
    
    //省略了构造器,get/set,toString
    
    @Override
    public int compareTo(Object obj){
    
    
        return id - ((Employee)obj).id;
    }
}

如果在后面又发现有新的需求,想要按照薪资排序,那么只能选择用定制排序,实现Comparator接口

class SalaryComparator implements Comparator{
    
    
    public int compare(Object o1, Object o2){
    
    
        Employee e1 = (Employee)o1;
        Employee e2 = (Employee)o2;
        if(e1.getSalary() > e2.getSalary()){
    
    
            return 1;
        }else if(e1.getSalary() < e2.getSalary()){
    
    
            return -1;
        }
        return 0;
    }
}

7.10 内部类

1、内部类的概念

声明在另外一个类里面的类就是内部类。

2、内部类的4种形式

(1)静态内部类

(2)非静态成员内部类

(3)有名字的局部内部类

(4)匿名内部类

7.10.1 匿名内部类

1、语法格式:

//在匿名子类中调用父类的无参构造
new 父类(){
    
    
    内部类的成员列表
}

//在匿名子类中调用父类的有参构造
new 父类(实参列表){
    
    
    内部类的成员列表
}

//接口没有构造器,那么这里表示匿名子类调用自己的无参构造,调用默认父类Object的无参构造
new 父接口名(){
    
    
    
}

2、匿名内部类、匿名对象的区别?

System.out.println(new Student("张三"));//匿名对象

Student stu = new Student("张三");//这个对象有名字,stu

//既有匿名内部类,又是一个匿名的对象
new Object(){
    
    
    public void test(){
    
    
        .....
    }
}.test();

//这个匿名内部类的对象,使用obj这个名字引用它,既对象有名字,但是这个Object的子类没有名字
Object obj = new Object(){
    
    
    public void test(){
    
    
        .....
    }
};

3、使用的形式

(1)示例代码:继承式

abstract class Father{
    
    
    public abstract void test();
}
class Test{
    
    
    public static void main(String[] args){
    
    
        //用父类与匿名内部类的对象构成多态引用
        Father f = new Father(){
    
    
            public void test(){
    
    
                System.out.println("用匿名内部类继承了Father这个抽象类,重写了test抽象方法")
            }
        };
        f.test();
    }
}

(2)示例代码:实现式

interface Flyable{
    
    
    void fly();
}
class Test{
    
    
    public static void main(String[] args){
    
    
        //用父接口与匿名内部类的对象构成了多态引用
        Flyable f = new Flyable(){
    
    
            public void fly(){
    
    
                System.out.println("用匿名内部类实现了Flyable这个接口,重写了抽象方法");
            }
        };
        f.fly();
    }
}

(3)示例代码:用匿名内部类的匿名对象直接调用方法

new Object(){
    
    
    public void test(){
    
    
        System.out.println("用匿名内部类的匿名对象直接调用方法")
    }
}.test();

(4)示例代码:用匿名内部类的匿名对象直接作为实参

Student[] all = new Student[3];
all[0] = new Student("张三",23);
all[1] = new Student("李四",22);
all[2] = new Student("王五",20);

//用匿名内部类的匿名对象直接作为实参
//这个匿名内部类实现了Comparator接口
//这个匿名内部类的对象,是定制比较器的对象
Arrays.sort(all, new Comparator(){
    
    
    public int compare(Obeject o1, Object o2){
    
    
        Student s1 = (Student)o1;
        Student s2 = (Student)o2;
        return s1.getAge() - s2.getAge();
    }
});

7.10.2 静态内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{
    
    
	【其他修饰符】 static class  静态内部类 【extends 静态内部类自己的父类】 【implements 静态内部类的父接口们】{
    
    
        静态内部类的成员列表;
	}
	
	外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求:

​ 可以包含类的所有成员

(2)修饰符要求:

  • ​ 权限修饰符:4种
  • ​ 其他修饰符:abstract、final

(3)使用外部类的成员上是否有要求

  • ​ 只能使用外部类的静态成员

(4)在外部类中使用静态内部类是否有要求

  • ​ 正常使用

(5)在外部类的外面使用静态内部类是否有要求

1)如果使用的是静态内部类的静态成员
		外部类名.静态内部类名.静态成员
(2)如果使用的是静态内部类的非静态成员
		①先创建静态内部类的对象
		外部类名.静态内部类名 对象名 = new 外部类名.静态内部类名(【实参列表】);
		②通过对象调用非静态成员
		对象名.xxx

(6)字节码文件形式:外部类名$静态内部类名.class

3、示例代码

class Outer{
    
    
    private static int i = 10;
    static class Inner{
    
    
        public void method(){
    
    
            //...
            System.out.println(i);//可以
        }
        public static void test(){
    
    
            //...
            System.out.println(i);//可以
        }
    }
    
    public void outMethod(){
    
    
        Inner in = new Inner();
        in.method();
    }
    public static void outTest(){
    
    
        Inner in = new Inner();
        in.method();
    }
}
class Test{
    
    
    public static void main(String[] args){
    
    
        Outer.Inner.test();
        
        Outer.Inner in = new Outer.Inner();
        in.method();
    }
}

7.10.3 非静态内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{
    
    
	【修饰符】 class  非静态内部类 【extends 非静态内部类自己的父类】 【implements 非静态内部类的父接口们】{
    
    
        非静态内部类的成员列表;
	}
	
	外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求:

​ 不允许出现静态的成员

(2)修饰符要求

​ 权限修饰符:4种

​ 其他修饰符:abstract,final

(3)使用外部类的成员上是否有要求

​ 都可以使用

(4)在外部类中使用非静态内部类是否有要求

​ 在外部类的静态成员中不能使用非静态内部类

(5)在外部类的外面使用非静态内部类是否有要求

//使用非静态内部类的非静态成员
//(1)创建外部类的对象
外部类名  对象名1 = new  外部类名(【实参列表】);

//(2)通过外部类的对象去创建或获取非静态内部类的对象
//创建
外部类名.非静态内部类名  对象名2 = 对象名1.new 非静态内部类名(【实参列表】);

//获取
外部类名.非静态内部类名  对象名2 = 对象名1.get非静态内部类对象的方法(【实参列表】);

//(3)通过非静态内部类调用它的非静态成员
对象名2.xxx

(6)字节码文件形式:外部类名$非静态内部类名.class

3、示例代码

class Outer{
    
    
    private static int i = 10;
    private int j = 20;
    class Inner{
    
    
        public void method(){
    
    
            //...
            System.out.println(i);//可以
            System.out.println(j);//可以
        }
    }
    
    public void outMethod(){
    
    
        Inner in = new Inner();
        in.method();
    }
    public static void outTest(){
    
    
       // Inner in = new Inner();//不可以
    }
    
    public Inner getInner(){
    
    
        return new Inner();
    }
}
class Test{
    
    
    public static void main(String[] args){
    
    
        Outer out = new Outer();
        
        Outer.Inner in1 = out.new Inner();     //创建   	
        in1.method();
        
        Outer.Inner in2 = out.getInner();	//获取
        in2.method();
    }
}

7.10.4 局部内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{
    
    
	【修饰符】 返回值类型  方法名(【形参列表】){
    
    
        【修饰符】 class  局部内部类 【extends 局部内部类自己的父类】 【implements 局部内部类的父接口们】{
    
    
        	局部内部类的成员列表;
		}
	}	
	外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求

​ 不允许出现静态的成员

(2)修饰符要求

​ 权限修饰符:不能

​ 其他修饰符:abstract、final

(3)使用外部类的成员等上是否有要求

​ ①使用外部类的静态成员:随便用

​ ②使用外部类的非静态成员:能不能用要看所在的方法是否是静态的

​ ③使用所在方法的局部变量:必须 final修饰的

(4)在外部类中使用局部内部类是否有要求

​ 有作用域

(5)在外部类的外面使用局部内部类是否有要求

​ 没法使用

(6)字节码文件形式:外部类名$编号局部内部类名.class

3、示例代码

class Outer{
    
    
    private static int i = 10;
    private int j = 20;

    
    public void outMethod(){
    
    
        class Inner{
    
    
            public void method(){
    
    
                //...
                System.out.println(i);//可以
                System.out.println(j);//可以
            }
   		}
        Inner in = new Inner();
        in.method();
    }
    public static void outTest(){
    
    
        final int k = 30;
       class Inner{
    
    
            public void method(){
    
    
                //...
                System.out.println(i);//可以
                System.out.println(j);//不可以
                System.out.println(k);//可以
            }
   		}
        Inner in = new Inner();
        in.method();
    }
}

Guess you like

Origin blog.csdn.net/weixin_43912367/article/details/117526411