Java 基础-面向对象

162 阅读16分钟

概述

类:对一类事物共同点的 描述
对象:是对某一个事物的一个 个体

描述说明

静态成员: 静态方法 静态属性
普通成员: 普通方法 普通属性

类的基本概念

访问修饰符

访问控制修饰符访问级别同类同包子类不同包
public公开可以\color{#00a73a}{可以}可以\color{#00a73a}{可以}可以\color{#00a73a}{可以}可以\color{#00a73a}{可以}
protected受保护啊可以\color{#00a73a}{可以}可以\color{#00a73a}{可以}可以\color{#00a73a}{可以}不可以\color{#a40303}{不可以}
默认可以\color{#00a73a}{可以}可以\color{#00a73a}{可以}不可以\color{#a40303}{不可以}不可以\color{#a40303}{不可以}
private私有可以\color{#00a73a}{可以}不可以\color{#a40303}{不可以}不可以\color{#a40303}{不可以}不可以\color{#a40303}{不可以}

属性和方法

  • 属性
    1. java 中,成员方法可以访问,静态属性,和方法,静态方法只能访问静态属性和方法
    // 一个简单的类
    public class BasicClass {
       public static String name = "lisa";
       public  int age = 10;
       public void hi(){
       }
       public  static void hello(){
       }
    }
    
    1. 属性的定义类型可以为任意类型,包含基本类型和引用类型

    2. 属性如果不赋值,有默认值(String:null,int:0,double:0.0,boolean:false 以此类推)

  • 方法
  1. 可变参数
    • 可以传入多个相同的参数
    • 可以参数的可以是0个或任意多个,参数可以为数组
    • 可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
    • 一个形参列表中只能出现一个可以参数
    public class BasicClass {
    
        public void sum( int... mums){
        }
    }
        BasicClass basicClass = new BasicClass()  
    
        // 可以参数的可以是0个或任意多个
        basicClass.sum()
        basicClass.sum(1, 2, 3)
    
        // 可以参数可以为数组
        int[] arr ={1,2,3}
        basicClass.sum(arr)
    

作用域

  • 说明

    1. 静态成员的作用域是整个类,普通成员的作用域是整个实现类,局部变量的作用域在代码块中
    2. 全局变量可以赋值,因为有默认值,局部变量必须赋值
  • 注意

    1. 全局变量和局部变量可以重名,访问域遵循就近原则
    2. 全局变量生命周期和类相同,局部变量和代码块相同

构造方法

  • 注意
    1. 构造方法遵循修饰符规则
    2. 构造方法没有返回值
    3. 构造方法是完成对象的初始化,并不是创建对象 (当调用构造方法的时候 类其实已经在堆里面存在了,调用构造方法其实只是给类赋值)
    4. 如果一个类中没有写构造方法,编译后会默认生成一个无参的构造方法,但是如果写了就不会生成默认的无参构造方法,所以 如果我们定义了一个有参构造,又需要调用无参构造,就需显示定义一个无参构造

this 和 super

  • this

    1. this 可以用来在静态变量(静态方法)和 成员变量(成员方法)重名的时候区分它们,加了this前缀就是 成员变量(成员方法)(注意在方法内的属性访问遵循就近原则)
    2. this只能在 成员变量(成员方法)中使用
    3. 构造方法中使用 this只能调用构造方法,在构造方法调用this,必须放置第一条语句
    // 构造方法中调用的例子
     public class BasicClass {
          String name = "a";
          BasicClass(){
          //  System.out.println("ad");
          //  必须放在第一行
          this("hai");
      }
    
      BasicClass(String name){}
    
      public  static void hello(){
        String name = "b";
        // 就近原则
        System.out.println(name); // 输出:b
        System.out.println(this.name); // 输出:a
      }
    
    
  • super

    1. super 可以访问遵循访问修饰符
    2. super在构造方法中和this只能出现一个,并且在第一行
    3. 子类的成员方法可以通过 super 使用父类方法
    4. super的访问不限于父类,是按照超类中的就近原则查找
  • 总结 | 区别 | this | super | | --- | --- | --- | | 访问属性 | 访问本类中的属性,如果本类没有此属性,
    则从父类继续查找 | 从父类开始查找 | | 调用方法 | 访问本类中的方法,如果本类没有此方法,
    则从父类继续查找 | 从父类开始查找 | | 调用构造方法 | 调用本类构造方法,必须放在构造方法首行 | 调用父类构造方法,必须放在构造方法首行 | | 意义 | 表示当前对象 | 子类中访问以父类开始的对象 |

代码块

  • 语法
  1. 修饰符可选,要写的话也只能写 static
  2. 代码块分为两类 静态代码块普通代码块
  3. 逻辑语句可以为任何逻辑语句,;可以忽略
  4. 代码块在构造方法之前执行,可以理解为是对构造方法的一种补充说明
  • 静态代码块什么时候被加载
  1. 静态代码块在类加载的时候执行,并且只会在第一次加载的时候执行
  2. 创建子类的时候,从顶层类开始加载
  3. 使用静态成员的时加载
  4. Animal dog = new Dog(),Animal类中的静态代码块也会执行
  5. 静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态属性初始化,按照定义的顺序调用
  6. 静态代码块只能调用静态成员
  • 普通代码块
  1. 普通代码块实例创建一次就会调用一次
  2. 普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,按照定义的顺序调用
  3. 普通代码块可以调用任意成员
  4. 子类实例创建的时候,从顶层类开始加载例子如下
public class Animal {
    {
        System.out.println(" Animal Block");
    }
    public Animal() {
        System.out.println(" new Animal");
    }
}
public class Dog extends Animal {
     {
        System.out.println(" Dog Block");
    }
    public Dog() {
        // super();
        // 调用普通代码块
        System.out.println("new Dog");
    }
}

    // 执行顺序
    // Animal Block
    // new Animal
    // Dog Block
    //  new Dog
  • 重点
    创建 C extends B extends A,三个对象,之心顺序如下
    1. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    2. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    3. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    4. 父类构造方法
    5. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    6. 子类构造方法
   public class A {
       static  {
           System.out.println("static A Block");
       }
       {
           System.out.println("A Block");
       }
       public A() {
           System.out.println("new A");
       }
   }
   public class B extends A{
       static  {
           System.out.println("static B Block");
       }
       {
           System.out.println("B Block");
       }

       public B() {
           System.out.println("new B");
       }
   }
   public class C extends B{
       static  {
           System.out.println("static C Block");
       }
       {
           System.out.println("C Block");
       }

       public C() {
           System.out.println("new C");
       }
   }
   //    执行顺序
   //    static A Block
   //    static B Block
   //    static C Block
   //    A Block
   //    new A
   //    B Block
   //    new B
   //    C Block
   //    new C

final

  • 说明
  1. final 是最后,最终的意思
  2. final 可以修饰类,属性,方法和局部变量
  3. 什么情况下使用 final
    • 当不希望类被继承时
      final public class A {}
      
    • 当不希望父亲的某些方法被子类重写时
      final public class A {
          public final void hi(){} 
          public static final void hi(){} 
      }
      
    • 当不希望类的某个属性的值被修改
      final public class A {
          public final name
          public static final name
      }
      
    • 当不希望某个局部变量被修改
      final public class A {
          public final void hi(){
              final double NUM = 0.8
          } 
      }
      
  • 注意
  1. final修饰的属性又叫常量,一般用 大写下划线来命名
  2. final修饰的属性在定义时,必须有初始值,并且之后不能修改 ( 静态属性,普通属性,构造方法,代码块)
  3. 如果final修饰的是静态属性,则初始化的位置只能是 ,定义时、在静态代码块,不能在构造方法中赋值
  4. final类不能继承,但是可以实例化对象
  5. 如果类不是final类,但是含有final方法,则该方法不能重写,类依然可以被继承
  6. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
  7. final不能修饰构造方法
  8. final 和 static 往往搭配使用,效率更高,底层编译器做了优化处理
        final public class A {
          public static final name = 1000;
          static {
            System.out.println("A static Block");
           }
        }
         System.out.println(A.nam); // 1000 ,A static Block 不会执行
    
  9. 包装类(Integer,Double,Float,Boolean等都是final),String也是final

继承

  • 注意
    1. 如果子类想访问超类的私有属性和方法,需要超类提供公共方法来访问
    2. 子类必须调用父类构造方法,完成父类的初始化
    3. 当创建子类时,不管使用子类的那个构造方法,一定会去调用父类的构造方法,如果父类没有无参构造方法,子类指定调用父类的哪个构造方法
    4. super 必须放在构造方法的第一行
    5. superthis 在构造方法中不能共存
    6. java 中所有的类都是都是 Object 的子类
        public class BasicClass {}
        public class Sub extends BasicClass {
            Sub(){
                // 调用父类构造方法,不写程序会自动添加 父类无参构造
                super();
            }
    
            /*
    
            public Sub() {
                this("a");
            }
    
            public Sub(String name) {
                // 原则是在调用构造起始处开始 调用 super()
                super();
            }
            */
    
        }
      ```
    

多态

方法多态

  • Overload 重载

    1. 方法名必须相同
    2. 参数列表必须不同(类型,个数,顺序至少一个不同)
    3. 返回类型无要求(返回类型不同不会构成方法的重载)
  • Override 重写

    1. 子类方法的方法名参数要和父类方法的的方法名参数完全一样
    2. 子类方法的返回类型要和父类方法返回类型一样,或者是父类返回类型的子类 比如:父类返回类型是 Object,子类方法返回类型是String
    3. 子类方法不能缩小父类方法的访问权限 private -> protected -> default -> public
  • 总结

    名称发生范围方法名形参列表返回类型修饰符
    Overload本类必须一样类型,个数,顺序至少一个不同无要求无要求
    Override父子类必须一样相同子类重写的方法,返回的类型和父类返回得了诶行一致,或者是其子类子类不能缩小父类方法的访问范围

类多态

  • 重点
  1. 一个对象的编译类型和运行类型可以不一致
  2. 编译类型在定义对象时,就确定了,不能改变
  3. 运行类型是可以变的
  4. 编译类型看定义时,编译类型(等号左边) = 运行类型(等号右边)
  • 向上转型

    1. 本质:父类的引用指向子类的对象
    2. 语法:父类类型 引用名 = new 子类类型();
    3. 特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(遵循访问修饰符规则),不能调用子类中的特有成员;最终运行类型看子类的具体实现
  • 向下转型

    1. 语法:子类类型 引用名 = new (子类类型)父类引用();
    2. 只能强转父类的引用,不能强转父类的对象
    3. 要求父类的引用必须指向的是当前目标类型的对象 (一般使用 instanceof)
    4. 当向下转型后,可以调用子类类型中所有成员

属性多态

  1. 属性没有重写只之说!属性的值看编译类型
public class Base {
    int count = 10
}
public class Sub extends Base {
   int count = 20
}
Base base = new Sub();
System.out.println(base.count); // 10
Base sub = new Sub();
System.out.println(sub.count); // 20

内部类

一个类的内部又完整嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员(属性,方法,构造器,代码块,内部类),内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。

局部内部类

  • 说明
    1. 可以直接范根外部类的所有成员,包含私有的
    2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰
    3. 作用域仅仅在定义它的方法或代码块中
    4. 局部内部类访问 --> 外部类的成员可以直接访问
    5. 外部类在方法中访问-->局部内部类的成员,需要创建对象再访问(必须在作用域内)
    6. 外部其其它类不能访问->局部内部类
    7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.普通成员,外部类名.静态成员)
        class InnerClass02{}
        public class OuterClass {
            private int n1 = 100;
            private void m2(){};
            public  void m1(){
                class InnerClass01 extends InnerClass02 {
                    public void f1() {
                        // 局部内部类访问 --> 外部类的成员可以直接访问
                        System.out.println(n1);
                        m2();
                    }
                }
                // 外部类在方法中访问-->局部内部类的成员,需要创建对象再访问(必须在作用域内)
                InnerClass01 innerClass01 = new InnerClass01();
                innerClass01.f1();
            };
    
        }
    

匿名内部类

  • 重点细节
    1. 本质是内部类,用完就会被回收
    2. 该类没有名字(实际是由系统自动自动生成,下面的例子, OuterClass1,1 , 1 就是系统生成的)
    3. 匿名内部类 可以基于类,接口,抽象类创建
    4. 可以直接访问外部类的所有成员,包含私有
    5. 不能添加访问修饰符,因为它的地位是一个局部变量
    6. 作用域仅仅在定义它的方法或代码块中
    7. 匿名内部类访问 --> 外部类的成员可以直接访问
    8. 外部其其它类不能访问->匿名内部类
    9. 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.普通成员,外部类名.静态成员)
        AnonymousInnerClass {
            public void say(){}
        }
        public class OuterClass {
            public void m2(){
                AnonymousInnerClass anonymousInnerClass = new AnonymousInnerClass() {
                    @Override
                    public void say(){
                        System.out.println("say:"+this.getClass()); // OuterClass$1
                    }
                };
    
                System.out.println(anonymousInnerClass.getClass()); // OuterClass$1
                anonymousInnerClass.say();
                
                 // 两种访问方式
                // new AnonymousInnerClass() {
                //            @Override
                //            public void say(){
                //                System.out.println("say:"+this.getClass()); // OuterClass$1
                //            }
                //        }.say();
            };
        }
    

成员内部类

    public class OuterClass{
        public class MemberClass{ 
        }
        public void m3(){
            new MemberClass();
        }
    }
    
    OuterClass outerClass = new OuterClass();
    outerClass.m3()
  • 说明
    1. 成员内部类是定义在外部类的成员位置,并且没有static修饰符
    2. 可以直接访问外部类的所有成员,包括私有
    3. 可以添加任意修饰符(private , protected, default , public),因为它是一个成员
    4. 作用域和其它成员一样
    5. 成员内部类访问 --> 外部类的成员可以直接访问
    6. 外部类在方法中访问-->成员部类,在方法内创建方法,再访问
    7. 外部其他类访问成员类的三种方式
          public class OuterClass{
              public class MemberClass{ 
              }
              public MemberClass getMemberClass(){
                 return new MemberClass();
              }
          }
      
          OuterClass outerClass = new OuterClass();
          // 方式一
          OuterClass.MemberClass memberClass = outerClass.new MemberClass();
          // 方式二
          outerClass.getMemberClass();
      
    8. 如果外部类和成员内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.普通成员,外部类名.静态成员)

静态内部类

     public class OuterClass{
      public static class StaticClass{};
     }
  • 说明
    1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问普通成员
    2. 可以添加任意修饰符(private , protected, default , public),因为它是一个成员
    3. 作用域和其他静态成员一样
    4. 可以访问外部类的所有静态成员
    5. 外部类要访问静态内部类,创建对象,再访问
           public class OuterClass{
            public static class StaticClass{};
            public StaticClass getStaticClass(){
                 return new StaticClass();
            }
            public static StaticClass _getStaticClass(){
                 return new StaticClass();
            }
           }
          // 方式一
              OuterClass outerClass = new OuterClass();
              outerClass.getStaticClass()
          // 方式二
              OuterClass.StaticClass staticClass = new OuterClass.StaticClass();
          // 方式三
              OuterClass._getStaticClass()
      
    6. 如果外部类和静态内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.静态成员)

抽象类

  • 说明
    1. 解决父类方法不确定性的问题
    2. 用abstract关键字来修饰一个类,抽象类自己不能创建实例
          public abstract class Animal{}
      
    3. 用abstract关键字类修饰一个方法时,这个方法就是抽象方法,没有方法体,抽象方法所在类必须是抽象类
          public abstract class Animal{
              public abstract void say();
          }
      
    4. 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
    5. 抽象类可以有任何成员(抽象类还是类,普通类有什么抽象类就有什么)
    6. 抽象方法不能有主体
    7. 如果一个类继承了抽象类,则必须实现抽象类的所有抽象方法,除非自己也声明为 abstract 类
    8. 抽象方法不能使用 private,final ,static 来修饰,因为这些关键字和重写相违背

接口

  • 说明
    1. 接口有,抽象方法(无方法体),静态方法(有方法体), 默认方法(有方法体)
    2. 在jdk7之前接口里面所有的方法都没有方法体,jdk8之后接口可以有默认方法,也就是说方法可以有具体实现(包含静态方法)
        public interface IA {
            String name = "hi";
            Integer age = 1;
    
            default public void hi1(){
                System.out.println("hi1");
            }
            public static void hi2(){
                System.out.println("hi2");
            }
    
            public static void hi3();
        }
        public class A implements IA{
             public static void hi3(){};
        }
    
        System.out.println(A.name); // hi
        System.out.println(A.age); // 1
        IA.hi2(); // hi2
        A a = new A();
        a.hi1(); // hi1
    
  • 注意
    1. 接口不能被实例化
    2. 接口中的所有方法都是 public 方法,接口中抽象方法,可以不用 abstract 修饰符,编译后会自动加上,(在子类中缩小修饰符的范围可以验证)
          // 实际上是如下
          public interface IA {
            abstract public void hi3();
        }
      
    3. 一个普通类实现接口,就必须将该接口的所有方法都是实现
    4. 抽象类实现接口,可以不用实现接口的方法
    5. 一个类可以实现多个接口
    6. 接口中的属性只能是 final的, 而且只能是 public static final 修饰符
       public interface IA {
      
           String name = "hi"; 
           // 实际如下,必须初始化
           // public static final String name = "hi";
      
      
         }
      
    7. 一个接口不能继承其它的类,但是可以继承多个别的接口
      interface IA extends IB,IC {}
      
    8. 接口的修饰符 只能是public 和默认,这和类的修饰符一样
    9. 接口可以多态,也就是说可以接口转型

枚举类

  1. 自定义枚举类

    public class Season {
        private String key;
        private String value;
    
        public Season(String key, String value) {
            this.key = key;
            this.value = value;
        }
    
        public String getKey() {
            return key;
        }
    
        public String getValue() {
            return value;
        }
    
        @Override
        public String toString() {
            return "Season{" +
                    "key='" + key + ''' +
                    ", value='" + value + ''' +
                    '}';
        }
    
        public final static Season A = new Season("keyA","valueA");
        public final static Season B = new Season("keyB","valueB");
        public final static Season C = new Season("keyC","valueC");
        public final static Season D = new Season("keyD","valueD");
    }
    
  2. enum 关键字枚举类

    public enum EnumSeason {
        A("keyA","valueA"),
       B("keyB","valueB"),
        C("keyC","valueC"),
        D("keyd","valueD"),
        E;
    
        private String key;
        private String value;
    
         private EnumSeason() {
            
        }
    
        private EnumSeason(String key, String value) {
            this.key = key;
            this.value = value;
        }
    
        public String getKey() {
            return key;
        }
    
        public String getValue() {
            return value;
        }
    
        @Override
        public String toString() {
            return "EnumSeason{" +
                    "key='" + key + ''' +
                    ", value='" + value + ''' +
                    '}';
        }
    }
    
  • 注意
    1. 当我们使用 enum 关键字创建枚举类的时候,会默认继承 Enum类
    2. 传统的 public final static Season A = new Season("keyA","valueA"); 简化成 A("keyA","valueA")
    3. 如果使用无参构造器创建枚举类,则实参列表和小括号都能省略,看上方案例E
    4. 当枚举类都好分割,不许放在首行
    5. 枚举不能继承其他类
    6. 枚举类和普通类一样可以实现接口支持 implements