Java面向对象 OOP

Object Oriented Programming

要解决的问题

  1. 单独的变量解决,不利于数据的管理,把某个对象的信息拆解了,例如:存储一只猫的名字,年龄var等信息

  2. 如果用数组,数据类型体现不出来,而且变量名和内容的对应关系体现不出来,例如:int只能用string来存,只能通过下标来获取信息

  3. 不能体现猫的行为,比如,数组的结构无法加入方法(函数)的处理方式

  4. 总之,不利于数据的管理,效率低

类与对象的概述

  1. 一个程序就是一个世界,有很多事物:对象[属性,行为]

  2. 数据类型分为Java提供的数据类型和自定义的数据类型

  3. 类就是一个数据类型

  4. 对象:可以通过一个类来创建一个对象

  5. 实例化:就是具体化,具体到一只猫

  6. 类是抽象的,概念的,代表一类事务:人类,猫类

  7. 对象是具体的,实际的,代表一个具体事物,即,是实例

  8. 类是对象的模板,对象是类的一个个体,对应一个实例

从类到对象的不同说法

  1. 创建一个对象

  2. 实例化一个对象

  3. 把类实例化

  4. 总之:类实例化 -> 对象

面向对象快速入门

  1. 使用OOP面向对象解决
  2. 实例化一只猫[创建一只猫对象]
public class Cat {
    public static void main(String[] args) {
        // 如何实例化一个对象
        // 1. new Catt(); 创建一只猫
        // 2. Catt cat1 = new Catt(); 把创建的猫赋给cat1
        Catt cat1 = new Catt();
        cat1.name = "abc";
        cat1.age = 3;
        cat1.color = "white";

        // 如何访问对象的属性
        System.out.println("第一只猫的信息" + cat1.name + " " + cat1.age + " " + cat1.color);

    }
}

class Catt {
    String name;
    int age;
    String color;
}

对象的内存布局,对象的存在形式

  1. new后产生的对象内(对象和数组都是引用类型),有一个指向堆中的地址
  2. 如果实例化的类中又有字符串等引用类型的变量,会有一个从堆中指向方法区的地址

属性

属性的概念

class Catt {
    String name; // name,age,color即为成员变量
    int age;
    String color;
    String[] master; //属性可以是基本数据类型,也可以是引用数据类型(对象,数组)
}
Catt cat = new Catt();
  1. 类中的变量即为属性,成员变量是用来表示属性的

  2. 成员变量 = 属性 = field

  3. 属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象,数组)。

  4. Catt类实例化后产生的cat实际上是对象引用,而不是真正的对象

  5. 真正的对象是class内部的属性:name,age,color,master

  6. 对象引用:实际上就是人的名字,小明,不是真正的属性,因为小明是个字符串名字,而不是一个人,引用相当于起了一个别名

  7. 对象:对象才是真正的人对象的胳膊腿就是属性

属性的定义

1.属性的定义语法同变量:访问修饰符 + 属性范围 + 属性名;

  1. 访问修饰符:public protected 默认 private ->访问范围逐渐缩小

对象

对象的创建

  1. 先声明再创建

    Cat cat; //先声明,声明的时候没有分配内存空间
    cat = new Cat(); //再创建,分配内存空间,实例化
  2. 直接创建

    Cat cat = new Cat(); // 类的类型就是实例化的类的类型
  3. 声明格式:定义变量的类 + 定义变量的名字

访问对象中的属性

  1. 基本语法:对象名.属性名

  2. 用 . 来取对象中的属性

    Person a = new Person();
    a.age = 10;
    a.name = "小明";
    Person b;
    b = a; // 
    b.age = 200;
    b.name = null; //将b在栈中的指向地址置为空,也就是说,原来a和b都指向堆中的同一个地址的指针,b断掉了
    System.out.println(a.age); // 这个时候输出a.age是经过b.age修改之后的200
    System.out.println(b.age); // 这个时候输出b.age会出现异常

成员方法

  1. 某些情况下,需要定义成员方法,简称方法
  2. 比如人类:除了由一些属性(年龄,姓名)以外,还要有一些行为,例如说话等
public class Method01 {
    public static void main(String[] args){
        //方法使用
        //1. 方法写好后,不去显式调用不会使用方法
        //2. 先创建对象,创建对象后和调用属性的方式相同调用方法
        for(int i = 0;i < 100; i ++){
        Person p1 = new Person();
        p1.speak();
        }
    }
}

class Person {
    String name;
    int age;

    // public 表示方法是公开的
    // void 表示方法没有返回值
    // {} 大括号内是方法体,嵌入执行代码
    public void speak() {
        System.out.println("abc");
    }
}
public class Method02 {
    public static void main(String[] args){
        Person p1 = new Person();
        p1.cal01();
        p1.cal02(5); //调用cal02方法,并且将5传给cal02fan
    }
}

class Person {
    public void cal01()
    {
        int res = 0;
        for (int i = 1; i < 1000; i++) {
            res += i;
        }
        System.out.println("计算结果" + res);
    }
    public void cal02(int n){ // (int n) 形参列表,表示当前有一个形参n,可以接受用户输入
        int res = 0;
        for(int i = 0;i <= n;i++){
            res += i;
        }
        System.out.println("res的结果为" + res);
    }
}
import java.util.Scanner;
public class Method02 {
    public static void main(String[] args){
        Person p1 = new Person();
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        p1.cal01();
        p1.cal02(n); //调用cal02方法,并且将5传给cal02方法
    }
}

class Person {
    public void cal01()
    {
        int res = 0;
        for (int i = 1; i < 1000; i++) {
            res += i;
        }
        System.out.println("计算结果" + res);
    }
    public void cal02(int n){ // (int n) 形参列表,表示当前有一个形参n,可以接受用户输入
        int res = 0;
        for(int i = 0;i <= n;i++){
            res += i;
        }
        System.out.println("res的结果为" + res);
    }
}
public int getSum(int num1,int num2){ //(int num1.int num2)形参列表,2个形参,可以接受用户传入的两个数
	int res = num1 + num2;
	return res; //表示把res的值,返回
}

方法的妙用

  1. 避免冗余度太高:当需要多次调用时,可以放到外面多次调用,提高代码的复用性
public class Method03 {
    public static void main(String[] args) {
        Tools tool = new Tools();
        int[][] map = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
        int res = tool.getSum(map);
        System.out.println(res);
    }
}

class Tools {
    public int getSum(int[][] map) {
        int res = 0;
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                res += map[i][j];
            }
        }
        return res;
    }
}
  1. 可以将实现的细节封装起来,然后供其它用户来调用即可

成员方法的定义

访问修饰符[public private protected] 返回数据类型[void int ...] 方法名 (形参列表){
	//方法体语句
	return 返回值;
}
  1. 如果不写,为默认访问修饰符,default

返回数据类型

  1. 一个方法最多返回一个值

  2. 如果想要返回多个结果,则需要返回一个数组,将结果封装到一个数组中去 (即将返回值设为int[]等数组类型)

    public class MethodDetail {
        public static void main(String[] args) {
            AA a = new AA();
            int[] res = a.getSumAndSub(1, 2);
            System.out.println("sum=" + res[0]);
            System.out.println("sub=" + res[1]);
        }
    }
    
    class AA{
        public int[] getSumAndSub(int num1,int num2){
            int[] res = new int[2];
            res[0] = num1 + num2;
            res[1] = num1 - num2;
            return res;//这个地方res是一个数组,所以应该返回一个地址
        }
    }
  3. 返回类型可以为任意类型,包括基本数据类型和引用类型(数组,对象)

  4. 返回值类型必须和return的值类型一致或兼容

  5. void的返回值可以写return,此时return后面不能带任何东西,默认return值为空

注意,方法体里面不能再定义方法

  1. 方法体里面不能再定义方法,不能嵌套应用(注意这个地方是不能定义,而不是不能使用,函数中可以使用函数,递归

方法调用的细节

  1. 同一个类中的方法调用,可以直接调用

    public class Method03 {
        public static void main(String[] args) {
            A a = new A();
            a.sayOk();
        }
    }
    
    class A {
        public void print(int n) {
            System.out.println("print方法被调用" + n);
        }
    
        public void sayOk() {
            print(10);
        }
    }
  2. 跨类中的方法A类调用B类方法:需要通过对象名调用(即在A类中再实例化一个变量B)

    public class Method03 {
        public static void main(String[] args) {
            A a = new A();
            a.sayOk();
            a.m1();
        }
    }
    
    class A {
        public void print(int n) {
            System.out.println("print方法被调用" + n);
        }
    
        public void sayOk() {
            print(10); //这个地方没有使用.去对象名调用,因为这个函数的定义在同一个类中
        }
        public void m1(){
            System.out.println("m1方法被调用");
            B b = new B();
            b.hi(); //这个地方进行了对象名调用,因为方法的定义不在同一个类中
        }
    }
    
    class B{
        public void hi(){
            System.out.println("B类中的hi()被执行");
        }
    }

重载OverLoad

  1. java中允许同一个类中,多个同名方法的存在,但是要求形参列表不一致
  2. 重载的好处:1. 减轻了起名的麻烦 2. 减轻了记名的麻烦

实例

  1. ```java
    public class Caculation01 {

      public static void main(String[] args) {
          Mycaculator m1 = new Mycaculator();
          System.out.println(m1.calculate(1.1, 2.2));
          System.out.println(m1.calculate(1, 2));
      }
    

    }

    class Mycaculator {

      public int calculate(int n1, int n2) { // 如果只是参数名不同则不能构成重载
          return n1 + n2;
      }
    
      public double calculate(int n1, double n2) {
          return n1 + n2;
      }
    
      public double calculate(double n1, double n2) {
          return n1 + n2;
      }
    

    }

    
    3.   参数顺序不一样也是重载,先int后double 和 先double后int不同
    
    ### 方法重载的细节
    
    1.   方法名必须相同
    2.   参数列表:必须不同(形参**类型**或**个数**或**顺序**,**至少有一样不同**,**参数名无要求**)
    3.   **返回类型不要求**(如果只是返回类型不同而其它都相同,则不构成重载)
    4.   能否构成重载,就看**在编译的时候是否报错**即可,如果报**已经定义了这个方法**的错误,就不构成重载
    
    ## 可变参数
    
    1.   java允许将同一个类中多个**同名同功能**但是参数个数不同的方法,封装成一个方法
    
    2.   ```java
         public class Var{
         	public static void main(String[] args){
                 HspMethod m1 = new HspMethod();
                 System.out.println(m1.sum());
             }	
         }
         class HspMethod{
             public int sum(int... nums){ 
               //1. ...表示接受的是可变参数,类型是int,即可以接受多个int(0-多),int... nums,int... nums
               //2. 使用可变参数时,可以当作数组来使用,即nums可以当作数组,即nums可以取下标,当作数组去处理
               //3. 遍历nums求和即可
               int res = 0;
               for(int i = 0;i < nums.length;i++){ //因为可变参数的参数类型可以当作数组去处理,所以可以取length
                   res += nums[i];
               }
                return res;
             }
         }
  2. 上面的可以通过重载来实现(函数名相同,但是参数不同)

  3. 也可以使用可变参数:方法名称相同,功能相同,参数个数不同 -> 使用可变参数优化

基本语法

访问修饰符 返回类型 方法名(数据类型... 形参名){}
  1. 注意这个地方数据类型后面的三个点是不能够更改的,是表明参数的个数是多个,省略号
  2. 数据类型和后面的三个点…是不能够分隔的

注意事项

  1. 可变参数的实参可以是0个或任意多个
  2. 可变参数的实参也可以直接是数组
  3. 可变参数可以和普通类型的参数一起放在形参列表,但是必须保证可变参数在最后,否则编译都会报错,更别提使用了
  4. 一个形参列表中只能有一个可变参数,并且需要放到参数列表的最后

构造方法

需求

  1. 前面的都是先创建好一个对象之后,再给他的年龄赋值
  2. 现在要求:在创建人类的对象时,直接指定这个对象的年龄和姓名,一次完成
  3. 构造器的主要目的:完成对新对象的初始化而不是创建新对象

基本语法

[修饰符] 方法名(形参列表){
	方法体
}
  1. 构造器的修饰符是可以默认的
  2. 构造器没有返回值只有构造器可以没有返回值,没有返回值的只有构造器
  3. 方法名必须和类名一样
  4. 参数列表和成员方法一样的规则
  5. 构造器的调用,由系统完成
  6. 一个类可以定义多个不同的构造器,即构造器重载,当构成重载时,未指定的参数将会赋值为默认值
  7. 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器,比如Dog(){},使用javap指令反编译可以查看源代码
  8. 一旦定义了自己的构造器默认的构造器就覆盖了,就不能再使用默认的构造器,除非显式地声明一下,和自己定义的构成重载

实例

public class Test02 {
    public static void main(String[] args) {
        // 当new一个对象时,直接通过构造器指定名字和年龄
        // 在new的时候直接传参,可以使用构造器
        Person p1 = new Person("smith", 80);
        // 构造器在被调用的时候,对象应该已经被创建了
    }
}

class Person {
    String name;
    int age;

    public Person(String pname, int page) {
        System.out.println("yes");
        name = pname;
        age = page;
    }
}
  1. 构造器在被调用的时候,对象已经被创建了,是通过new 类(),括号进行传参

Javap的使用

  1. javap时jdk提供的一个命令行工具,Javap能对给定的class文件提供字节代码进行反编译

  2. 通过它,可以查看到源代码

  3. 格式:

    javap xxx.class 或者 javap xxx (可以不带.class)
  4. -c 对代码进行反汇编

  5. -v 输出附加信息

  6. -p 显式所有类和成员

IDEA

Eclipse

IDEA使用技巧

  1. 更改编码

  2. 源码一定要放到src目录下

  3. 新建一个类时,new java class的时候,不需要带上.java的文件名后缀

  4. 只需要写清类名,后面自动填上.java

IDEA常用快捷键

  1. 快速格式化的快捷键:ctrl+alt+L,可以快速补齐代码

模板快捷键

  1. 模板可以高效地完成开发,提高效率

应用场景

作用

  1. 可以区分相同名字的类
  2. 当类很多时,可以很好地管理类(某个类在某个包中,好搜索)

包的基本语法

package com.hspedu; package 包的名字;
  1. package 关键字,表示打包
  2. com.hspedu 表示包名

包的原理

  1. 包的本质就是创建不同的文件夹来保存文件

快速入门

  1. 打包:选中src -> new -> Package
  2. 点号操作符 . 是用来向下继续引目录的,例如在src下创建包com.abc,是src下有com,com下有abc
  3. 当有一个相同类名被实例化时,想要实例化另一种类,那么需要用 . 操作符来标识是哪个包(就是加上包名)
  4. 如果引用两个相同名字的类,编译器会报错,不能同时引两个Dog

包的命名规则

  1. 只能包含数字,字母,下划线,小圆点,但是不能用数字开头,不能是关键字或保留字
  2. 即不能包含class等关键字
  3. 一般是:小写字母 + 小圆点
  4. 一般是 com.公司名.项目名.业务模块名,最后一级一般是类的名字

导入包

  1. 语法:import 包;
  2. 不要忘记import最后的分号
  3. 引入的主要目的是:使用该包下面的类
  4. 和python的import相同,可以只导入包中的某个类,也可以使用*去导入所有的包
  5. 需要使用哪个类就引入哪个类,不一次全部引入某个包中的所有类
  6. package的作用是声明当前类所在的包,需要放在类的最上面一个文件的最上面),一个类中最多只有一句package
  7. 一个类中最多只有一句package,即一个类只能属于一个包
  8. import指令位置放在package的下面在类定义的前面,可以有多句且没有顺序的要求

修饰符

  1. 类似于一个阶梯图形
  2. 同类就是本类

封装

  1. 封装就是把抽象出的数据**[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法]**,才能对数据进行操作

好处

封装的实现步骤,三步走

  1. 属性私有化 private 不能直接修改属性

  2. 提供一个公共的set方法,用于对属性进行判断并且赋值

    public void setXxx(类型 参数名){
    	//加入数据验证的业务逻辑
    	如果通过则
    	属性=参数名
    }
  3. 提供要给公共的get方法,用于获取属性的值

    public 数据类型 getXxx(){
    	//权限判断
    }

自己写set和get太慢,可以使用快捷键,alt + insert,getter and setter

注意事项

  1. set方法返回值为void,不能再写一个变量,赋值给它

  2. 并且p1的age是private类型的,不能由其它类访问,所以p1.age本身就是错误的

  3. 上面就是set方法,又去赋值,是错误的

  4. 在调用set方法时,只需要去传参即可

this关键字

目的

  1. 构造器的形参,如果能够直接写成属性名

    public Dog(String name,int age){
    	name = name;
    	age = age;
    }
  2. 但是出现了一个问题,根据变量的作用域原则,构造器的name是局部变量而不是属性,构造器的age也是局部变量而不是属性

  3. 这样做相当于是name局部变量又赋值给了它本身,没有用处,当这个构造器结束时,局部变量的作用域一结束,就什么都没了

什么是this

  1. this代表当前对象
  2. 形象说法:老韩说我的,是指老韩;小明说我的,是指小明,不同的对象指向不同的this
public Dog(String name,int age){
	//所以上面的name和age应该加上this,this.age就是当前对象的属性age
	this.name = name;
	this.age = age;
}

this小结

  1. 简单地说:哪个对象调用,this就代表哪个对象

命名问题

  1. 担心命名时使用了关键字,可以在名字后面加上下划线__

继承

为什么需要继承

  1. 编写两个类,一个大学生,一个大学毕业生,发现两个类的属性和方法有很多是相同的
  2. 继承提高代码复用性

快速入门

  1. 多个类存在相同的属性和方法时,可以从这些类中抽象出父类在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可

  2. 父类中包含子类中所有属性和方法,子类中可以包含不同的特有属性

  3. 可以继续继承下一层会包含上一层的所有属性

  4. 我的理解:extends相当于复制了父类中的所有属性和方法

继承细节

第一:私有属性不能直接在子类中访问

  1. 子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,要通过公共的方法去访问
  2. 访问父类中的private属性可以写一个getter,return值写private的值,和封装的原理相同

第二:父类和子类的构造器调用问题

  1. 子类必须调用父类的构造器,完成父类的初始化
  2. 也就是说,在main函数中实例化子类时,是首先进行父类的构造器调用,然后再进行子类的构造器调用(初始化)

第三:在父类中没有默认的无参构造器时

  1. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过

第四:指定调用父类的某个构造器

  1. 指定地去调用父类的某个构造器,则需要显式的调用

  2. ```java
    super(参数列表)

    
    3.   调用父类中的**无参构造器**,super()中的参数什么都不写,默认调用super()
    
    #### 第五:super的使用
    
    1.   super在使用时,**必须放在构造器的第一行**
    2.   super关键字**只能在构造器中使用,不能在普通成员方法中使用**
    
    #### 第六:
    
    1.   super()和this()都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器
    
    #### 第七:object类
    
    1.   Java所有类都是Object的子类,Object是所有类的基类
    2.   按住 **ctrl + h** 可以看到所有类的父子关系
    3.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220822171532.png)
    4.   如果new了最后一个子类,会**一级一级地调用上一级的构造器**,直到**调用到了顶级父类的构造器**,**Object类**
    5.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220822171916.png)
    
    #### 第九:单继承
    
    1.   子类最多**只能继承一个父类**,即Java中式**单继承机制**
    2.   如果想让A继承B和C,则可以让**A继承B,B继承C**
    
    #### 第十:不能滥用继承
    
    1.   不能滥用继承,子类和父类之间必须满足 **is-a** 的逻辑关系
    2.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220822172523.png)
    
    ## super
    
    ### 概念
    
    1.   super代表父类的引用,用于访问父类的属性,方法,构造器
    
    ### 具体用法
    
    1.   访问父类的属性,但是不能访问父类的private属性,可以
    
         ```java
         super.属性名
  3. 访问父类的方法,不能访问父类的private方法

    super.方法名(参数列表)
  4. 调用父类的构造器(因为只有一个父类,所以不需要加父类的名字就可以区分),当参数不同时,调用语法:

    super(参数);

方法的重写 override

概念

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

实例

package Override;

public class Override01 {
    public void cry(){
        System.out.print("动物叫唤");
    }
}
//父类
package Override;

public class Dog extends Override01 {
    public void cry() {
        System.out.print("小狗汪汪叫");
    }
}
//子类
  1. 因为Dog是Animal子类
  2. Dog的cry方法和Animal的cry定义形式一样(名称,返回类型,参数)
  3. 这时,我们就说Dog的cry方法,重写了Animal的cry方法

方法重写细节

  1. 方法重写也叫方法覆盖,需要满足下面的条件

  2. 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样,否则不构成重写

  3. 子类方法的返回类型和父类一样,或者,是父类返回类型的子类,比如父类返回类型是Object,子类方法返回类型是String

  4. ```java

      public Object cry() {
          return null;
      }//父类
          public String cry(){
          return null;
      }//子类
    
    
    5.   否则会报错![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220822181100.png)
    
    6.   子类方法不能缩小父类方法的访问权限 public -> protected -> 默认 -> private
    
    7.   也就是说,返回类型可以细化,但是访问范围不能缩小
    
    8.   例如:父类中为public , 子类中为private,则会报错
    
    9.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220822181730.png)
    
    
    
    ## 多态polymorphic
    
    ### 解决的问题
    
    1.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220822222119.png)
    
    ```java
    package A;
    
    public class Master {
        private String name;
    
        public Master(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        //给小狗吃骨头
        public void feed(Dog dog,Bone bone){ //这个地方的参数,和int a一样,只是声明了数据类型和变量名
            System.out.print("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
        }
        //给小猫吃🐟,和给狗吃骨头构成重载
        public void feed(Cat cat,Fish fish){
            System.out.print("主人" + name + "给" + cat.getName() + "吃" + fish.getName());
        }
        //如果有很多动物和食物,会导致feed重载量很大
    }
    
package A;

public class Poly01 {
    private String name;
    public Poly01(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
}

class Fish extends Poly01{
    public Fish(String name){
        super(name);
    }
}

class Bone extends Poly01{
    public Bone(String name){
        super(name);
    }
}
package A;

public class Animal {
    private String name;
    public Animal(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
}

class Cat extends Animal{
    public Cat(String name) {
        super(name);
    }
}

class Dog extends Animal{
    public Dog(String name) {
        super(name);
    }
}
  1. 如果动物很多,食物很多,会导致feed方法很多(重载很多方法),不利于管理和维护
  2. 但是本质就是给某个动物喂某种食物
  3. 多态可以降低代码复用性

多态的基本介绍

  1. 多:多种
  2. 态:状态
  3. 方法或对象具有多种形态,是面向对象的第三大特征
  4. 多态是继承在封装和继承基础之上的

方法的多态

  1. 重写和重载就体现多态
  2. 重载传入不同的参数,就会调用不同的方法,就体现了多态
  3. 重写调用不同的对象,不同对象的方法为重写,体现了多态

对象的多态(核心)

重要的几句话(背过)

  1. 一个对象的编译类型和运行类型可以不一致

    一个父类的引用可以指向一个子类的引用

  2. 编译类型在定义对象时,就确定了,不能改变

  3. 运行类型是可以变化的

  4. 编译类型看定义时 = 的左边,运行类型看 = 的右边

体验对象多态的特点,编译类型和运行类型

  1. 编译时,javac,类型由声明该变量时使用的类型决定

  2. 运行时,java,类型由实际赋给该变量的对象决定

  3. ```java
    Person p = new Women(); //Women继承自Person类

    
    4.   上面的p会得到Person类的属性,调用Women类的方法
    
    5.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220823001726.png)
    
    6.   可以理解为:编译类型决定了变量的属性,而运行类型决定了方法
    
    ### 使用多态机制,可以统一的管理主人喂食的问题
    
    ## 抽象方法
    
    ### 处理问题:
    
    1.   处理的主要问题是:父类方法的不确定性问题
    
    2.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220826211645.png)
    
    3.   考虑将该方法设计为抽象(abstract)方法
    
    4.   所谓抽象方法就是没有实现的方法,所谓没有实现,就是没有方法体
    
    5.   当遇到
    
    6.   ```java
         Missing method body , or declare abstract
  4. 时,说明如果没有方法体,可以声明为一个抽象方法

  5. 抽象方法只需要声明即可

  6. ```java
    public abstract void eat();
    限制范围 abstract 返回值类型 函数名();

    
    10.   将一个类做成抽象类,并让子类去具体实现,父类只是起到一个声明的作用
    
    11.   一般来说,抽象类会被继承,由其子类来继承
    
    ### 抽象类细节
    
    1.   用abstract关键字来修饰一个类时,这个类就叫做抽象类
    
    2.   ```java
         访问修饰符 abstract 类名{
         	//属性
         	//方法
         	public abstract void eat(){}
         }
  7. abstract关键字修饰一个方法时,这个方法就是抽象方法

  8. ```java
    访问修饰符 abstract 返回类型 方法名(参数列表); //后面不能加方法体,否则会报abstract methods cannot have a body

    
    5.   抽象类的**价值更多作用在于设计**,是设计者设计好后,让**子类继承并实现**
    
    6.   后面的**接口**是很常见的抽象类调用
    
    7.   抽象类不能被实例化
    
    8.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220826212813.png)
    
    9.   **抽象类不一定要包含abstract方法**,也就是说,抽象类可以没有abstract方法。抽象类中还可以有实现的方法
    
    10.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220826213103.png)
    
    11.   **一旦一个类包含了abstract方法,则这个类必须声明为abstract**(但是一个abstract类中不一定包含abstract方法)
    
    12.   **抽象类可以有任意成员**  [抽象类的**本质还是类**]  ,比如:非抽象方法,构造器,静态属性等等
    
    13.   抽象方法不能有主体,即不能实现
    
    14.   如果**一个类继承了抽象类**,则它**必须实现抽象类的所有抽象方法**,**除非它自己也声明为abstract类**
    
    15.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220826214526.png)
    
    ### abstract修饰的对象
    
    1.   abstract只能修饰类和方法
    2.   abstract不能修饰属性和其它,抽象的属性说不通
    3.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220826213451.png)
    
    
    
    ## 接口interface
    
    ### 快速入门
    
    1.   usb插槽就是现实的接口
    2.   不用担心哪个插槽是插哪个的,因为做usb插槽的厂家和做各种设备的厂家都遵守了统一的规定,包括尺寸,排线等等
    3.   程序就是一个世界
    
    ### 实现思路
    
    1.   实现接口,就是把接口方法实现
    2.   接口方法就是一个标准,类似于USB2.0和USB3.0
    
    
    
    ### 概念
    
    1.   **接口就是给出一些没有实现的方法**,封装到一起,到某个类**要使用的时候**,再根据**具体情况把这些方法写出来**
    
    2.   ```java
         访问范围修饰符 interface 接口名{
         	//属性
             //方法,这个时候方法只有方法名,没有方法体的具体实现
         }
         class 类名 implements 接口{
             //自己属性
             //自己方法,这个时候可以根据具体情况加上类的调用
             //必须实现的接口的抽象方法
         }
  9. 在接口中,抽象方法,可以省略abstract关键字

  10. implement关键字用来调用接口,生效,执行,实施

  11. 在jdk8后,可以有默认实现方法,需要使用default关键字修饰

    default public void ok(){
    	
    }
  12. 在jdk7.0前,接口里所有方法都没有方法体,即都是抽象方法

  13. 在jdk8.0以后,接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现

接口的应用场景

  1. 当不同的程序员在开发时,如果写了不同的方法,综合的人调用起来就会很麻烦
  2. 所以接口的目的就是指定一个标准,接口只去定义后来的方法名,而不去定义具体方法
  3. 根据接口写类的程序员根据接口,再去写具体的方法,但是方法名不会有区别
  4. 最后,整合的程序员实例化对象,并且通过这个实例化出来的对象,调用这个对象的成员方法

接口的注意事项

  1. 接口不能被实例化,所以new interface_name是错误的
  2. 接口中所有的方法都是public方法,所以可以不写public关键字,默认的就是public(目的就是能够让所有的编写类的程序员都能够访问到接口)
  3. 接口中的抽象方法,可以不用abstract来修饰,下图可以看到,public和abstract都是灰色的,即可有可无的
  4. 一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter的快捷键去快捷调用所有接口中的方法
  5. 因为在继承时,子类不能缩小父类的访问范围,所以因为接口(父类)访问范围是public,子类的访问范围也就是public,如果访问范围限制为default或更小,则会报错
  6. 抽象类实现接口,可以不用实现接口的方法

类变量

待解决问题

快速入门

  1. 定义所有对象共享的变量即为类变量

  2. ```java
    类名.变量名 Child.count
    或者也可以
    实例化的对象.变量名 child1.count

    
    4.   可以**直接由类名访问**,因为是类变量
    
    5.   也可以由**实例化的对象进行类变量的访问**,因为这几个对象出自一个类,所以 这几个对象的变量是**共享的**
    
    6.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220828154057.png)
    
    
    
    ### 什么是类变量
    
    1.   **类变量也叫静态变量,静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的值都是相同的值,同样,任何一个该类的对象去修改它时,修改的也是同一个变量**
    
    2.   ```java
         访问修饰符 static 数据类型 变量名;
  3. 访问类变量:类名.类变量名 或者 对象名.类变量名

  4. 类变量并不依赖于实例,类变量是随着类的加载而创建的,所以没有创建对象实例也可以访问

  5. ```java
    public class lei {

      public static void main(String[] args){
          //这个地方是通过 类名.变量名 去访问的,类变量
          sta.a++;
          System.out.print(sta.a);
      }
    

    }

    class sta{

      public static int a = 0;
    

    }

    
    ### 类变量使用细节
    
    #### 使用类变量的时候
    
    1.   在需要**这个类的所有对象都能访问**到这个变量的时候就需要使用类变量(共享一个变量)
    2.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220828160025.png)
    
    #### 类变量和实例变量的区别
    
    1.   类变量是该类的**所有对象共享的**,而实例变量是**某个对象独享的**
    2.   实例对象如果没有实例化则不能访问
    3.   类变量如果没有实例化仍可以访问
    
    #### 加上static称为类变量或者静态变量,否则称为实例变量、普通变量、非静态变量
    
    #### 推荐使用  类名.类变量名  来访问
    
    ### 类变量的生命周期
    
    1.   类变量的声明周期是**随着类的加载开始,随着类的消亡而毁灭**
    2.   只要类没有消亡,类变量就不会消亡
    
    ## 类方法
    
    ### 静态方法
    
    1.   ```java
         访问修饰符 static 数据返回类型 方法名(){}
  6. 当方法使用了static修饰后,该方法就是静态方法

  7. 静态方法就可以访问静态属性/变量

main方法语法

解释main方法的形式

public static void main(String[] args){}

重点

  1. main方法是jvm虚拟机调用
  2. java虚拟机在执行main()方法时,不必创建对象,(类似于类方法,可以让这个类的所有对象都能访问到,共享),所以该方法必须是static,即在调用主类的main方法时,是没有必要去实例化这个主类的,只需要调用main方法(也没有见过实例化主类,为了调用main方法)
  3. java虚拟机需要调用类的main方法,所以该方法的访问权限必须是public
  4. 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
  5. 可以对main函数中的args取下标,来获取到main函数的参数的具体的值
  6. 实际是将main函数的参数打包成一个字符串,然后传给main函数
  7. java 执行的程序 参数1 参数2 参数3

main的特别说明

  1. 在main方法中,可以直接调用main方法所在的类的静态方法或静态属性
  2. 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
  3. 静态方法,可以访问本类的静态成员
  4. 静态方法,不可以访问本类的非静态成员(static的类,不能访问非static的成员变量,即不能访问非static的属性和方法)
  5. 如果想要访问非静态成员,则必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
  6. 静态方法main,要访问本类的非静态成员,需要先创建对象,再调用即可
  7. 如果main方法和要调用的非静态成员在一个类中,则需要将这个main方法和非静态成员共存的class实例化
  8. 实例化前:
  9. 后:
  10. 访问方法和调用成员方法相同,使用 . 去访问

java数据结构

链表的实现

  1. java ListNode链表是用Java自定义实现的链表结构

  2. ```java
    class ListNode{

      int val;
      ListNode next;
    

    }

    
    4.   添加构造方法,方便初始化
    
         ```java
         class ListNode{
         	int val;
         	ListNode next;
         	ListNode(int val){
         		this.val = val;
         	}
         }
  3. 删除一个节点

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public void deleteNode(ListNode node) {
            ListNode p = node.next;
            node.val = p.val;
            node.next = p.next;
        }
    }

Java注解

  1. @Override 告诉编辑器这个方法是覆盖父类的方法,在子类继承父类的时候用到,重写的时候用到
  2. @WebServlet(“/test”) 表示这个类是一个Servlet,Web容器就会识别这个注解,在运行的时候调用它,涉及到在使用tomcat就会调用它
  3. @Controller(“/test”) 表示某个类是一个控制器,告诉Spring框架该类是一个控制器

注解和注释

  1. 注释和注解不同,注解会影响程序的运行,而注释编译器则不会对其操作
  2. 注释是给开发人员看的,注解不是给人看的,是用于给程序看的,会影响程序的编译和运行,编辑器,框架,tomcat

使用

  1. 自定义开发一个Web容器,基本功能是加载Servlet,需要管理它的生命周期,所以必须先识别程序中的哪些类是Servlet
  2. 程序启动的时候,扫描所有的类,找出添加了@WebServlet注解的类,进行加载
  3. @WebServlet是在程序运行的时候起作用的,Java就把它的作用范围规定为RUNTIME
  4. @Override是给编译器看的,编译器工作的时候识别出了包含@Override注解的方法,就去检查上层父类的相关方法,存在则通过,否则报错

开发常用

@RequestMapping

  1. @RequestMapping可以将HTTP请求映射到某个类中的方法

  2.   @RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
      @ResponseBody
      public String getFoosBySimplePath() {
          return "Get some Foos";
      }
      //value值写路径,method写方法
    
  3. 详细参数看(100条消息) SpringMVC-@RequestMapping的参数和用法_流烟默的博客-CSDN博客_@requestmapping

  4. 最重要的参数:value=”url”,method= RequestMethod.POST/RequestMethod.GET