前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java面向对象OOP

Java面向对象OOP

作者头像
h3110_w0r1d
发布2024-02-19 19:50:24
1100
发布2024-02-19 19:50:24
举报

Java面向对象 OOP

Object Oriented Programming

要解决的问题

  1. 单独的变量解决,不利于数据的管理,把某个对象的信息拆解了,例如:存储一只猫的名字,年龄var等信息
  2. 如果用数组,数据类型体现不出来,而且变量名和内容的对应关系体现不出来,例如:int只能用string来存,只能通过下标来获取信息
  1. 不能体现猫的行为,比如,数组的结构无法加入方法(函数)的处理方式
  2. 总之,不利于数据的管理,效率低

类与对象的概述

  1. 一个程序就是一个世界,有很多事物:对象[属性,行为]
  2. 数据类型分为Java提供的数据类型和自定义的数据类型
  3. 类就是一个数据类型
  4. 对象:可以通过一个类来创建一个对象
  5. 实例化:就是具体化,具体到一只猫
  6. 类是抽象的,概念的,代表一类事务:人类,猫类
  7. 对象是具体的,实际的,代表一个具体事物,即,是实例
  8. 类是对象的模板,对象是类的一个个体,对应一个实例

从类到对象的不同说法

  1. 创建一个对象
  2. 实例化一个对象
  3. 把类实例化
  4. 总之:类实例化 -> 对象

面向对象快速入门

  1. 使用OOP面向对象解决
  2. 实例化一只猫[创建一只猫对象]
代码语言:javascript
复制
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. 如果实例化的类中又有字符串等引用类型的变量,会有一个从堆中指向方法区的地址

属性

属性的概念

代码语言:javascript
复制
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
  1. 对象引用:实际上就是人的名字,小明,不是真正的属性,因为小明是个字符串名字,而不是一个人,引用相当于起了一个别名
  2. 对象:对象才是真正的人对象的胳膊腿就是属性

属性的定义

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

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

对象

对象的创建

先声明再创建

代码语言:javascript
复制
Cat cat; //先声明,声明的时候没有分配内存空间
cat = new Cat(); //再创建,分配内存空间,实例化

直接创建

代码语言:javascript
复制
Cat cat = new Cat(); // 类的类型就是实例化的类的类型

声明格式:定义变量的类 + 定义变量的名字

访问对象中的属性

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

用 . 来取对象中的属性

代码语言:javascript
复制
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. 比如人类:除了由一些属性(年龄,姓名)以外,还要有一些行为,例如说话等
代码语言:javascript
复制
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");
    }
}
代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
public int getSum(int num1,int num2){ //(int num1.int num2)形参列表,2个形参,可以接受用户传入的两个数
	int res = num1 + num2;
	return res; //表示把res的值,返回
}

方法的妙用

  1. 避免冗余度太高:当需要多次调用时,可以放到外面多次调用,提高代码的复用性
代码语言:javascript
复制
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. 可以将实现的细节封装起来,然后供其它用户来调用即可

成员方法的定义

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

一个方法最多返回一个值

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

代码语言:javascript
复制
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是一个数组,所以应该返回一个地址
    }
}

返回类型可以为任意类型,包括基本数据类型和引用类型(数组,对象)

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

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

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

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

方法调用的细节

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

代码语言:javascript
复制
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);
    }
}

跨类中的方法A类调用B类方法:需要通过对象名调用(即在A类中再实例化一个变量B)

代码语言:javascript
复制
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. 减轻了记名的麻烦

实例

```java public class Caculation01 {

代码语言:javascript
复制
  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 {

代码语言:javascript
复制
  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;
  }

}

代码语言:javascript
复制
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;
         }
     }

上面的可以通过重载来实现(函数名相同,但是参数不同)

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

基本语法
代码语言:javascript
复制
访问修饰符 返回类型 方法名(数据类型... 形参名){}
  1. 注意这个地方数据类型后面的三个点是不能够更改的,是表明参数的个数是多个,省略号
  2. 数据类型和后面的三个点…是不能够分隔的
注意事项
  1. 可变参数的实参可以是0个或任意多个
  2. 可变参数的实参也可以直接是数组
  3. 可变参数可以和普通类型的参数一起放在形参列表,但是必须保证可变参数在最后,否则编译都会报错,更别提使用了
  1. 一个形参列表中只能有一个可变参数,并且需要放到参数列表的最后

构造方法

需求

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

基本语法

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

实例

代码语言:javascript
复制
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的使用

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

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

格式:

代码语言:javascript
复制
javap xxx.class 或者 javap xxx (可以不带.class)

-c 对代码进行反汇编

-v 输出附加信息

-p 显式所有类和成员

IDEA

Eclipse

IDEA使用技巧

  1. 更改编码
  1. 源码一定要放到src目录下
  1. 新建一个类时,new java class的时候,不需要带上.java的文件名后缀
  1. 只需要写清类名,后面自动填上.java

IDEA常用快捷键

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

模板快捷键

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

应用场景

作用

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

包的基本语法

代码语言:javascript
复制
package com.hspedu; package 包的名字;
  1. package 关键字,表示打包
  2. com.hspedu 表示包名

包的原理

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

快速入门

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

包的命名规则

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

导入包

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

修饰符

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

封装

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

好处

封装的实现步骤,三步走

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

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

代码语言:javascript
复制
public void setXxx(类型 参数名){
	//加入数据验证的业务逻辑
	如果通过则
	属性=参数名
}

提供要给公共的get方法,用于获取属性的值

代码语言:javascript
复制
public 数据类型 getXxx(){
	//权限判断
}

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

注意事项

  1. set方法返回值为void,不能再写一个变量,赋值给它
  2. 并且p1的age是private类型的,不能由其它类访问,所以p1.age本身就是错误的
  1. 上面就是set方法,又去赋值,是错误的
  2. 在调用set方法时,只需要去传参即可

this关键字

目的

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

代码语言:javascript
复制
public Dog(String name,int age){
	name = name;
	age = age;
}

但是出现了一个问题,根据变量的作用域原则,构造器的name是局部变量而不是属性,构造器的age也是局部变量而不是属性

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

什么是this

  1. this代表当前对象
  2. 形象说法:老韩说我的,是指老韩;小明说我的,是指小明,不同的对象指向不同的this
代码语言:javascript
复制
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. 父类中包含子类中所有属性和方法,子类中可以包含不同的特有属性
  1. 可以继续继承下一层会包含上一层的所有属性
  2. 我的理解:extends相当于复制了父类中的所有属性和方法

继承细节

第一:私有属性不能直接在子类中访问
  1. 子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,要通过公共的方法去访问
  1. 访问父类中的private属性可以写一个getter,return值写private的值,和封装的原理相同
第二:父类和子类的构造器调用问题
  1. 子类必须调用父类的构造器,完成父类的初始化
  2. 也就是说,在main函数中实例化子类时,是首先进行父类的构造器调用,然后再进行子类的构造器调用(初始化)
第三:在父类中没有默认的无参构造器时
  1. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过
第四:指定调用父类的某个构造器

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

```java super(参数列表)

代码语言:javascript
复制
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.属性名

访问父类的方法,不能访问父类的private方法

代码语言:javascript
复制
super.方法名(参数列表);

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

代码语言:javascript
复制
super(参数);

方法的重写 override

概念

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

实例

代码语言:javascript
复制
package Override;

public class Override01 {
    public void cry(){
        System.out.print("动物叫唤");
    }
}
//父类
代码语言:javascript
复制
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方法

方法重写细节

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

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

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

```java

代码语言:javascript
复制
  public Object cry() {
      return null;
  }//父类
      public String cry(){
      return null;
  }//子类
代码语言:javascript
复制
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重载量很大
}
代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
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. 重载传入不同的参数,就会调用不同的方法,就体现了多态
  1. 重写调用不同的对象,不同对象的方法为重写,体现了多态

对象的多态(核心)

重要的几句话(背过)
  1. 一个对象的编译类型和运行类型可以不一致 一个父类的引用可以指向一个子类的引用
  1. 编译类型在定义对象时,就确定了,不能改变
  2. 运行类型是可以变化的
  1. 编译类型看定义时 = 的左边,运行类型看 = 的右边

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

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

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

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

代码语言:javascript
复制
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

时,说明如果没有方法体,可以声明为一个抽象方法

抽象方法只需要声明即可

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

代码语言:javascript
复制
10.   将一个类做成抽象类,并让子类去具体实现,父类只是起到一个声明的作用

11.   一般来说,抽象类会被继承,由其子类来继承

### 抽象类细节

1.   用abstract关键字来修饰一个类时,这个类就叫做抽象类

2.   ```java
     访问修饰符 abstract 类名{
     	//属性
     	//方法
     	public abstract void eat(){}
     }

abstract关键字修饰一个方法时,这个方法就是抽象方法

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

代码语言:javascript
复制
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 接口{
         //自己属性
         //自己方法,这个时候可以根据具体情况加上类的调用
         //必须实现的接口的抽象方法
     }

在接口中,抽象方法,可以省略abstract关键字

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

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

代码语言:javascript
复制
default public void ok(){
	
}

在jdk7.0前,接口里所有方法都没有方法体,即都是抽象方法

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

接口的应用场景

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

接口的注意事项

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

类变量

待解决问题

快速入门

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

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

代码语言:javascript
复制
4.   可以**直接由类名访问**,因为是类变量

5.   也可以由**实例化的对象进行类变量的访问**,因为这几个对象出自一个类,所以 这几个对象的变量是**共享的**

6.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220828154057.png)



### 什么是类变量

1.   **类变量也叫静态变量,静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的值都是相同的值,同样,任何一个该类的对象去修改它时,修改的也是同一个变量**

2.   ```java
     访问修饰符 static 数据类型 变量名;

访问类变量:类名.类变量名 或者 对象名.类变量名

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

```java public class lei {

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

}

class sta{

代码语言:javascript
复制
  public static int a = 0;

}

代码语言:javascript
复制
### 类变量使用细节

#### 使用类变量的时候

1.   在需要**这个类的所有对象都能访问**到这个变量的时候就需要使用类变量(共享一个变量)
2.   ![](https://strongwillpro.oss-cn-beijing.aliyuncs.com/img/20220828160025.png)

#### 类变量和实例变量的区别

1.   类变量是该类的**所有对象共享的**,而实例变量是**某个对象独享的**
2.   实例对象如果没有实例化则不能访问
3.   类变量如果没有实例化仍可以访问

#### 加上static称为类变量或者静态变量,否则称为实例变量、普通变量、非静态变量

#### 推荐使用  类名.类变量名  来访问

### 类变量的生命周期

1.   类变量的声明周期是**随着类的加载开始,随着类的消亡而毁灭**
2.   只要类没有消亡,类变量就不会消亡

## 类方法

### 静态方法

1.   ```java
     访问修饰符 static 数据返回类型 方法名(){}

当方法使用了static修饰后,该方法就是静态方法

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

main方法语法

解释main方法的形式

代码语言:javascript
复制
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的属性和方法)
  1. 如果想要访问非静态成员,则必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
  2. 静态方法main,要访问本类的非静态成员,需要先创建对象,再调用即可
  3. 如果main方法和要调用的非静态成员在一个类中,则需要将这个main方法和非静态成员共存的class实例化
  4. 实例化前:
  1. 后:
  1. 访问方法和调用成员方法相同,使用 . 去访问

java数据结构

链表的实现

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

```java class ListNode{

代码语言:javascript
复制
  int val;
  ListNode next;

}

代码语言:javascript
复制
4.   添加构造方法,方便初始化

     ```java
     class ListNode{
     	int val;
     	ListNode next;
     	ListNode(int val){
     		this.val = val;
     	}
     }

删除一个节点

代码语言:javascript
复制
/**
 * 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

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

代码语言:javascript
复制
  @RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
  @ResponseBody
  public String getFoosBySimplePath() {
      return "Get some Foos";
  }
  //value值写路径,method写方法

详细参数看(100条消息) SpringMVC-@RequestMapping的参数和用法_流烟默的博客-CSDN博客_@requestmapping

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java面向对象 OOP
    • Object Oriented Programming
      • 要解决的问题
        • 类与对象的概述
          • 从类到对象的不同说法
            • 面向对象快速入门
              • 对象的内存布局,对象的存在形式
                • 属性
                  • 属性的概念
                  • 属性的定义
                • 对象
                  • 对象的创建
                  • 访问对象中的属性
                • 成员方法
                  • 方法的妙用
                  • 成员方法的定义
                  • 注意,方法体里面不能再定义方法
                  • 方法调用的细节
                • 重载OverLoad
                  • 实例
                • 构造方法
                  • 需求
                    • 基本语法
                      • 实例
                    • Javap的使用
                      • IDEA
                        • Eclipse
                          • IDEA使用技巧
                          • IDEA常用快捷键
                          • 模板快捷键
                          • 应用场景
                          • 作用
                          • 包的基本语法
                          • 包的原理
                          • 快速入门
                          • 包的命名规则
                          • 导入包
                        • 修饰符
                          • 封装
                            • 好处
                            • 封装的实现步骤,三步走
                            • 自己写set和get太慢,可以使用快捷键,alt + insert,getter and setter
                            • 注意事项
                          • this关键字
                            • 目的
                            • 什么是this
                            • this小结
                          • 命名问题
                            • 继承
                              • 为什么需要继承
                              • 快速入门
                              • 继承细节
                            • 方法的重写 override
                              • 概念
                              • 实例
                              • 方法重写细节
                              • 多态的基本介绍
                              • 方法的多态
                              • 对象的多态(核心)
                              • 体验对象多态的特点,编译类型和运行类型
                              • 接口的应用场景
                              • 接口的注意事项
                            • 类变量
                              • 待解决问题
                              • 快速入门
                            • main方法语法
                              • 解释main方法的形式
                              • 重点
                              • main的特别说明
                            • java数据结构
                              • 链表的实现
                            • Java注解
                              • 注解和注释
                              • 使用
                              • 开发常用
                          相关产品与服务
                          容器服务
                          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档