Java面向对象高级

一、static 关键字

1. static 修饰成员变量

1)类变量:有 static 修饰,属于类,在计算机里只有一份, 会被类的全部对象共享。

2)实例变量(对象的变量):无 static 修饰,属于对象。

public class CLASS {
    static String name;
    int age;
}

public class Test {
    public static void main(String[] args) {
        // 类名.类变量
        CLASS.name = "wyb";
        // 对象名.实例变量
        CLASS c = new CLASS();
        c.age = 13;
    }
}

2. static 修饰成员方法

1)类方法:有 static 修饰,属于类。

2)实例方法(对象的方法):无 static 修饰,属于对象。

补充:main 方法

main 方法也属于一种类方法,它也可以传入参数。

在程序执行时,JVM虚拟机会直接调用 main 方法。

3. static 的注意事项

1)类方法中可以直接访问类的成员,不可以直接访问实例成员。

2)实例方法中既可以直接访问类成员,也可以直接访问实例成员。

3)实例方法中可以出现 this 关键字,类方法中不可以出现 this 关键字的。

( this 是用来指向调用方法的对象,而类方法只针对类)

4.代码块

代码块是类的5大成分之一(成员变量,成员方法,构造器,代码块,内部类)

1)静态代码块

格式:static { }

特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。

作用:完成类的初始化,例如对类变量的初始化赋值。

public class CLASS {
    static String name;
    static int age;
    static {
        name = "wyb";
        age = 20;
    }
}
public class Test {
    public static void main(String[] args) {
        System.out.println(CLASS.name);
        System.out.println(CLASS.age);
    }
}

2)实例代码块

格式:{ }

特点:每次创建对象时,执行实例代码块,并在构造器前执行。

作用:和构造器一样,都是用来完成对象的初始化的,例如对实例变量进行初始化赋值。

5.单例设计模式

作用:确保一个类只有一个对象

(1)饿汉式单例(创建类时创建对象)

方法:1)把类的构造器私有(为了使外部无法创建对象)

           2)定义一个类变量,记住类的对象

           3)定义一个类方法,返回对象

public class CLASS {
    private static CLASS student = new CLASS();
    private CLASS() {}
    public static CLASS getStudent()
    {
        return student;
    }
}

(2)懒汉式单例(需要对象时创建对象)

方法:1)把类的构造器私有(为了使外部无法创建对象)

           2)定义一个类变量用于存储类的对象

           3)定义一个类方法,第一次调用时创建对象,再返回对象

public class CLASS {
    private static CLASS student;
    private CLASS() {}
    public static CLASS getStudent()
    {
        if(student == null)
        {
            student = new CLASS();
        }
        return student;
    }
}

二、继承

1.继承的基本使用

定义:利用 extends 关键字,使一个类和另一个类建立父子关系

特点:子类能够继承父类的非私有成员变量和成员方法

对象的创建:由子类和父类共同完成

public class A {
    public int a;
    public void print1()
    {
        System.out.println("--public print--");
    }
    private int b;
    private void print2()
    {
        System.out.println("--private print--");
    }
}
public class B extends A{
    public int b;
    public void print3()
    {
        System.out.println(a);
        print1();

        //System.out.println(b);
        //print2();
    }
}
public class Test {
    public static void main(String[] args) {
        B b = new B();

        System.out.println(b.a);
        //System.out.println(b.b);
        b.print1();
        //b.print2();
        b.print3();
    }
}

2.权限修饰符

作用:限制类中的成员能够被访问的范围

分类:public    private    protected   【缺省】

Java面向对象高级_第1张图片

3.单继承

Java只有单继承,不支持多继承,但可以多层继承

例:A => B => C    C可以继承A和B的非私有成员

public class A {
    public int i;
    public void print1()
    {
        System.out.println("--public print--");
    }
    private int j;
    private void print2()
    {
        System.out.println("--private print--");
    }
}
public class B extends A{
    public int k;
    public void print3()
    {
        System.out.println(a);
        print1();

        //System.out.println(b);
        //print2();
    }
}
public class C extends B{
    public void print4()
    {
        System.out.println(i);
        //System.out.println(j);
        System.out.println(k);
    }
}
public class Test {
    public static void main(String[] args) {
        C c = new C();
        c.print1();
        //c.print2();
        c.print3();
        c.print4();
    }
}

4. Object 类

Object 类是Java中所有类的祖类,Object 类不需要在创建类时声明(默认继承)

Object 类的常用方法有:hascode,toString

1)toString 

能够返回对象的地址,在打印对象本体时默认调用,一般用于重写 toString 方法后返回对象内容

5.方法重写

作用:当父类方法需求不能满足子类时,子类可以重写一个与父类方法名称和参数列表相同的方法

注意事项:1)重写之后,方法的访问遵循就近原则

                  2)重写小技巧: 使用 @Override 注解,他可以指定 java 编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。

                  3)子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限。

                  4)重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。

                  5)私有方法、静态方法不能被重写,如果重写会报错的。

public class A {
    public int i;
    public void print()
    {
        System.out.println("666");
    }

    public void func(int a,int b)
    {
        System.out.println(a+b);
    }
}

public class B extends A{
    @Override
    public void print()
    {
        System.out.println("abc");
    }
    @Override
    public void func(int a,int b)
    {
        System.out.println(a-b);

    }
}

应用:重写 Object 类的 toString 方法

6.子类的成员访问特点

1)就近原则

2)访问本类成员用this,访问父类成员用super

7.子类构造器

特点:调用子类构造器前,会先调用父类的无参构造器,在执行自己,即在创建子类构造器时默认包含了 super()

class X{
    public X() {
        System.out.println("1");
    }
}
class Y extends X{
    public Y() {
        //super(); 子类构造器默认存在
        System.out.println("2");
    }
    public Y(int a) {
        //super(); 子类构造器默认存在
        System.out.println("3");
    }
}

注意:1)如果父类没有无参构造器,程序会出错,可以手动书写 super() 函数;

           2)如果想要调用父类的有参构造器,一定要创建同参子类构造器并声明super()

           3)super() 只能在构造器第一行声明

8. this() 调用兄弟构造器

class Student{
    public Student(){
    }
    public Student(String name,int age)
    {
        this(name,age,60);
    }
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    public String name;
    public int age;
    public int score;
}

注意:同 super() 一样,this() 也只能在构造器第一行声明,并且这两者不能同时在一个构造器里声明

三、多态

1.认识多态

Java中分为对象多态和行为多态,但是属性(成员变量)无多态

必须有相对的继承关系才能实现多态操作,并且子类要求对父类的方法重写

class People{
    public String name = "People";
    public void showClass(){
        System.out.println("People方法");
    }
}
class Teacher extends People{
    public String name = "Teacher";
    public void showClass(){
        System.out.println("Teacher方法");
    }
}
class Student extends People{
    public String name = "Student";
    public void showClass(){
        System.out.println("Student方法");
    }
}
public class Test {
    public static void main(String[] args) {
        People p1 = new Teacher();  // 对象多态
        System.out.println(p1.name);// 成员变量无多态
        p1.showClass();             // 行为多态

        People p2 = new Student();
        System.out.println(p2.name);// 编译看左边,运行也看左边
        p2.showClass();             // 编译看左边,运行看右边
    }
}

注意:编译时只能调用父类的成员方法,但运行时执行的是子类重写后的成员方法

2.多态的优点

1)在多态的形式下,new的对象是解耦合的

//People p = new Teachar();
People p = new Student();

new 创建的对象类型随时可以改变 

2)方法在调用对象时,只需要通过父类的对象类型接收,即可接收所有多态形式下的子类对象

public class Test {
    public static void main(String[] args) {
        People p1 = new Teacher();
        People p2 = new Student();
        printName(p1);
        printName(p2);
    }
    public static void printName(People p){
        System.out.println(p.getName());
    }
}

3.多态的缺点

在多态形式下,无法访问子类独有的成员方法

解决方案:强制类型转换(只能转换相对应的子类类型)

public class Test {
    public static void main(String[] args) {
        People p1 = new Teacher();
        Teacher t1 = (Teacher) p1;
        People p2 = new Student();
        Teacher t2 = (Teacher) p2;
    }
}

代码如上,p1 完成了类型转换,但 p2 报错

补充:instanceof 关键字,用于判断对象类型是否对应

        People p1 = new Teacher();
        if(p1 instanceof Teacher) {
            Teacher t1 = (Teacher) p1;
        }
        else {
            Student s1 = (Student) p1;
        }

四、补充

1. final 关键字

修饰类:该类被称为最终类,特点是不能被继承了。

修饰方法:该方法被称为最终方法,特点是不能被重写了。

修饰变量:该变量有且只能赋值一次。

public class Test {
    // final修饰静态成员变量 -> 常量
    public static final String S_NAME = "cxk";
    // final修饰实例成员变量
    private final int age = 11;
    
    public static void main(String[] args) {
        // final修饰基本类型变量
        final String name = "fzc";
        // final修饰引用类型变量
        final int[] arr = {1,2,3};
    }
}

注意:1)final修饰的静态成员变量即为常量

           2)final修饰基本类型的变量,该变量的值不可改变

           3)final修饰引用类型的变量,该变量的地址不可改变

2.常量

使用 static final 修饰的成员变量即为常量

作用:通常用于记录系统的配置信息

命名规范:单词全部大写,单词与单词之间用_连接

优点:1)代码可读性更好,可维护性也更好。

2)程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。

五、抽象类

1.认识抽象类

1)abstract 关键字:用于修饰类和成员方法

abstract class People{
    public abstract void fun();
}

注意:修饰的成员方法不能有方法体

2.抽象类的特点

1)抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。

2)类该有的成员(成员变量、方法、构造器)抽象类都可以有。

3)抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。

4)一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。

3.使用抽象类的好处

最大的优势在于能够更好地支持多态

class Teacher extends People{
    public Teacher(String name) {
        super(name);
    }
    @Override
    public void action() {
        System.out.println("老师在教书");
    }
}
class Student extends People{
    public Student(String name) {
        super(name);
    }
    @Override
    public void action() {
        System.out.println("学生在学习");
    }
}
abstract class People{
    private String name;
    public People(String name) {
        this.name = name;
    }
    public void printName() {
        System.out.print(name);
    }
    public abstract void action();
}
public class Test {
    public static void main(String[] args) {
        People p1 = new Student("fzc");
        p1.printName();
        p1.action();
        People p2 = new Teacher("cxk");
        p2.printName();
        p2.action();
    }
}

上述代码为 People 父类下,Student 和 Teacher 两个子类对象,通过抽象方法重写,来创建对象

4.应用场景:模版方法设计模式

用于解决大量的重复代码

1)定义一个抽象类。

2)在里面定义2种方法:模板方法 -- 把相同代码放里面去。

                                        抽象方法 -- 具体实现交给子类完成。

上一小节的代码就已经体现出来了这种设计模式,这里不再举例

注意:如果模版方法不再修改,建议用 final 修饰

六、接口

1.认识接口

1)interface 关键字:用于定义接口

interface People{
    // 默认为常量,不需要static final修饰
    String NAME = "cxk";
    // 默认为抽象方法,不需要abstract修饰
    void action();
}

接口中只能定义成员变量(常量)和成员方法(抽象方法),且不能直接创建对象

功能:用来被类实现(implements),实现接口的类叫做实现类

public interface A {
    void testA1();
    void testA2();
}
public interface B {
    void testB1();
    void testB2();
}
// 实现类
public class C implements A,B{
    @Override
    public void testA1() {}
    @Override
    public void testA2() {}
    @Override
    public void testB1() {}
    @Override
    public void testB2() {}
}

注意:一个类可以实现多个接口,但必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。

2.接口的好处

1)弥补了类单继承的不足,一个类在继承其他类的同时,也可以实现多个接口。

2)让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。

3.接口综合案例

Java面向对象高级_第2张图片

1)创建一个 Student 类,用于存储单个学生的信息

public class Student {
    private String name;
    private String sex;
    private double score;
    public Student() {
    }
    public Student(String name, String sex, double score) {
        this.name = name;
        this.sex = sex;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
}

包含三种属性,对应的 get,set 方法和用于初始化的构造器

2)创建一个 ClassManager 类,用于存储全部学生信息和实现题目需求的操作

public class ClassManager {
    private ArrayList students = new ArrayList<>();
    public ClassManager() {
        students.add(new Student("A","男",100));
        students.add(new Student("B","女",60));
        students.add(new Student("C","女",77));
        students.add(new Student("D","男",88));
    }
    public void printInfo(){
    }
    public void printScore(){
    }
}

使用 ArrayList 集合存储学生信息,利用构造器初始化,初步定义两个实现功能的方法

3)因为两种功能各有两种方案,所以利用面向接口编程思想,通过接口创建实现类

public interface StudentOperator {
    void printInfo(ArrayList students);
    void printScore(ArrayList students);
}

4)创建两个实现类

public class StudentOperatormpl1 implements StudentOperator{
    @Override
    public void printInfo(ArrayList students) {
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println("姓名:"+s.getName()+" 性别:"+s.getSex()+" 分数:"+s.getScore());
        }
    }
    @Override
    public void printScore(ArrayList students) {
        double allsroce = 0;
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            allsroce += s.getScore();
        }
        System.out.println("平均分为:"+allsroce/students.size());
    }
}
public class StudentOperatormpl2 implements StudentOperator{
    @Override
    public void printInfo(ArrayList students) {
        int manCount = 0,womanCount = 0;
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println("姓名:"+s.getName()+" 性别:"+s.getSex()+" 分数:"+s.getScore());
            if(s.getSex().equals("男")){
                manCount += 1;
            }
            else{
                womanCount += 1;
            }
        }
        System.out.println("男生人数为:"+manCount+"女生人数为"+womanCount);
    }
    @Override
    public void printScore(ArrayList students) {
        double allsroce = 0,max = 0,min = 100;
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            allsroce += s.getScore();
            max = max>s.getScore()?max:s.getScore();
            min = min

5)在 ClassManager 类下调用实现类的功能

public class ClassManager {
    private ArrayList students = new ArrayList<>();
    //private StudentOperator studentOperator = new StudentOperatormpl1();
    private StudentOperator studentOperator = new StudentOperatormpl2();
    public ClassManager() {
        students.add(new Student("A","男",100));
        students.add(new Student("B","女",60));
        students.add(new Student("C","女",77));
        students.add(new Student("D","男",88));
    }
    public void printInfo(){
        studentOperator.printInfo(students);
    }
    public void printScore(){
        studentOperator.printScore(students);
    }
}

6)进行测试

public class Test {
    public static void main(String[] args) {
        ClassManager cs = new ClassManager();
        cs.printInfo();
        cs.printScore();
    }
}

补充:步骤五的实现利用了接口的多态创建实现类的对象

private StudentOperator studentOperator = new StudentOperatormpl2();

注意:接口不能直接利用接口名创建对象,但可以通过多态的形式,利用实现类来创建对象

4.接口新增方法(JDK8之后)

1)默认方法(实例方法):使用 default 修饰,默认被 public 修饰,只能使用接口的实现类来调用

2)私有方法:必须使用 private 修饰,用于被接口中其他方法调用

3)接口方法(静态方法):使用 static 修饰,默认被 public 修饰,可以直接通过接口调用

public interface A {
    default void test1() {
        test2();
    }
    private void test2(){

    }
    static void test3(){

    }
}

5.接口的继承(多继承)

Java中接口可以继承,并且可以多继承

interface A{
    void test1();
}
interface B{
    void test2();
}
interface C extends B,A{
    
}
class D implements C{
    @Override
    public void test1() {
    }
    @Override
    public void test2() {
    }
}

注意事项:

1)一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。

2)一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。

3)一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。

4)一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

七、内部类

是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块)

定义:如果一个类定义在另一个类的内部,这个类就是内部类。

应用场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。

1.成员内部类【了解】

将内部类视为外部类的一个成员,内部类可以随意调用外部类的成员,外部类也可以创建内部类对象来访问内部类

public class Test {
    public static void main(String[] args) {
        Outer o1 = new Outer();
        o1.test1();
        Outer.Inner o2 = new Outer().new Inner();
        o2.test2();
    }
}
class Outer{
    private int a = 100;
    public void test1() {
        Inner i1 = new Inner();
        i1.test2();
    }
    public class Inner{
        private int a = 90;
        public void test2(){
            int a = 80;
            System.out.println(a);
            System.out.println(this.a);
            System.out.println(Outer.this.a);
        }
    }
}

注意:1)在外部类的外部创建内部类对象,要下面这种形式:

Outer.Inner o2 = new Outer().new Inner();

           2)如果外部类、内部类、内部类方法定义的变量重名,要逐级访问,如下:

System.out.println(a);
System.out.println(this.a);
System.out.println(Outer.this.a);

2.静态内部类【了解】

public class Test {
    public static void main(String[] args) {
        Outer.Inner o2 = new Outer.Inner();
        o2.test2();
    }
}
class Outer{
    private int a = 100;
    public static int b = 10;
    public static class Inner{
        public void test2(){
            //System.out.println(a);
            System.out.println(b);
        }
    }
}

注意:1)静态内部类可以直接访问外部类的静态变量,但不可以直接访问实例变量

           2)静态内部类创建对象不需要以外部类的对象为基础来创建了

        Outer.Inner o2 = new Outer.Inner();

3.局部内部类【了解】

定义在方法,代码块,构造器中的内部类(目前不需要学)

4.匿名内部类【重点】

1)匿名内部类:用于直接创建一个对象的临时类,一般配合抽象类和接口使用

public class Test {
    public static void main(String[] args) {
        People t = new People() {
            @Override
            public void action() {
                System.out.println("老师在教书");
            }
        };
        t.action();
    }
}
abstract class People{
    public String name;
    public abstract void action();
}

注意:创建匿名内部类时,Java首先会编译出来一个子类,根据这个子类来直接创建一个对象

2)匿名内部类的应用场景

通常作为一个参数传输给方法

八、枚举

1.认识枚举

枚举是一个特殊的类

public enum A {
    X,Y,Z;
    public String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class Test {
    public static void main(String[] args) {
        // A a = new A();
        A a1 = A.X;
        System.out.println(a1);// 可以直接打印枚举常量名

        A a2 = A.Y;
        a2.setName("cxk");
        System.out.println(a2.getName());

        A[] as = A.values(); // 将枚举类的所有常量存储到一个数组中
        A a3 = A.valueOf("Z"); // 利用对应的常量名获取常量
        System.out.println(a3.name()); // 获取对应的常量名
        System.out.println(a3.ordinal()); // 获取对应的索引
    }
}

 根据上述代码,枚举类有以下特点:

1)枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。

2)枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。

3)枚举都是最终类,不可以被继承。

4)枚举类中,从第二行开始,可以定义类的其他各种成员。

5)编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法 。

2.抽象枚举

在枚举中定义抽象方法

public enum A {
    X("cxk"){
        @Override
        public void showName() {
            System.out.println(getName());
        }
    },
    Y("fzc"){
        @Override
        public void showName() {
            System.out.println(getName());
        }
    };
    A() {
    }
    A(String name){
        this.name = name;
    }
    public abstract void showName();
    public String name;
    public String getName() {
        return name;
    }
}

注意:定义抽象枚举时,必须对常量对象进行方法重写

3.常见的应用场景

常用于表示一组信息,对功能进行标记和分类

例:以男女分类,创建不同的功能

enum Constant{
    BOY,GIRL;
}

public class Test {
    public static void main(String[] args) {
        check(Constant.BOY);
        check(Constant.GIRL);
    }
    public static void check(Constant sex) {
        switch (sex)
        {
            case BOY:
                System.out.println("看美女~~");
                break;
            case GIRL:
                System.out.println("看帅哥~~");
                break;
        }
    }
}

九、泛型

1.认识泛型

在定义类、接口、方法时,同时声明了一个或者多个类型变量(如:) ,称为泛型类、泛型接口、泛型方法,它们统称为泛型。

作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力。这样可以避免强制类型转换,及其可能出现的异常。

泛型的本质:把具体的数据类型作为参数传给类型变量。

2.泛型类

public class Test {
    public static void main(String[] args) {
        myArrayList arrayList1 = new myArrayList();
        myArrayList arrayList2 = new myArrayList<>();
    }
}

class myArrayList{
    private Object[] arr = new Object[10];
    private int size;
    public boolean add(E e){
        arr[size++] = e;
        return true;
    }
    public E get(int index) {
        return (E)arr[index];
    }
}

注意:1)泛型类可以声明多个泛型

class myClass1{
}

           2)泛型类声明泛型时可以添加限制条件

class myClass2{
}

3.泛型接口

和泛型类使用大同小异

class People{
}
class Teacher extends People{
}
interface PeopleData{
    void add(E e);
    String getName(E e);
}
class TeacherData implements PeopleData{
    @Override
    public void add(Teacher teacher) {
    }
    @Override
    public String getName(Teacher teacher) {
        return null;
    }
}

注意:泛型接口也可以声明多个泛型,也可以加限制条件

4.泛型方法

class Car{
}
class BWM extends Car{
}
class BENZ extends Car{
}
    public static void test(ArrayList e){
    }

泛型方法也可以声明多个泛型,也可以加限制条件

补充:

1)?通配符:用于代表泛型,可以替代一切类型

上面的代码也可以改成以下形式:

public static void test(ArrayList e){
}

2)泛型上下限

 ? extends Car 即为上限,接收的是 Car 类及其子类

 ? super Car 即为下限,接收的是 Car 类及其父类

5.泛型的注意事项

1)泛型是工作在编译阶段的,一旦程序编译成class文件, class文件中就不存在泛型了,这就是泛型擦除。

2)泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。

你可能感兴趣的:(JavaSE学习,java,开发语言)