Java面向对象(中)

面向对象(中)

一、包

1.包的本质

包实际上就是创建不同的文件夹来保存类文件。主要是为了相同名字的类的使用,引出包。

2.包的命名

①命名规则
只能包含数字,字母,下划线,小圆点,但不能以数字开头,不能用关键字或者保留字。
②命名规范
一般是字母+小圆点。
一般模板是 com.公司名(个人名).项目.业务模块 。

3.常用的包

java.lang.* :lang包是基本包,默认导入,不需要再引入。
java.util.* :util包系统提供的工具包,工具类。
java.net.* :网络包,网络编程。
java.awt.* :Java界面开发。

4.包的导入

1.基本语法:import 包;
因为我们引入一个包主要是使用该包下的某个内容,所以最好只导入那个内容。
以Scanner为例
我们最好只导入Scanner。如下:

import java.util.Scanner; // 表示只会引入java.util.Scanner类

而不是全部导入,如下:

import java.util.*; // 表示导入java.util下的所有内容

5.包的细节

1.package的作用是声明当前类所在的包,需要写在class文件的最上方,一个类最多只有一行package
2.import指令,位置在package的下面,定义在类的前面,可以多句,没有顺序要求。

二、访问修饰符

1.基本介绍

Java提供四种访问控制修饰符,用于控制方法和属性的访问权限(范围)。
  ①公开级别:用public修饰,对外公开。
  ②受保护级别:用protected修饰,对子类的同一个包的类公开。
  ③默认级别:没有修饰符号,向同一个包的类公开。
  ④私有级别:用private修饰,只有类本身可以访问,不对外公开。

2.访问修饰符对应表格

访问级别访问修饰符同类同包子类不同包
公开public
受保护protected×
默认没有修饰符××
私有private×××

3.修饰符细节

1.修饰符可以用来修饰类中属性,方法,以及类。
2.只有默认和public可以修饰类,并且遵循上面表格的规则。
3.子类访问继承的时候再说
4.方法和属性规则是一样的。
举例同包下有A与B两个类
A类,代码如下:

package com.hh6879.modifier;

public class A {
    // 四个属性分别使用不同的修饰符修饰
    public int num1 = 1;
    protected int num2 = 2;
    int num3 = 3;
    private int num4 = 4;

    public void f1() {
        // 本类方法可以访问 四个属性
        System.out.println("num1 = " + num1 + " num2 = " + num2 + " num3 = " + num3 + " num4 = " + num4);
    }

}

B类,代码如下:

package com.hh6879.modifier;

public class B {
    public void f2(){
        A a = new A(); // 本包下可以访问public
        // 在同一个包下, 可以访问public, protected, 没有修饰符(默认), 不能访问private
        System.out.println("num1 = " + a.num1 + " num2 = " + a.num2 + " num3 = " + a.num3); 
        // 访问不了num4, a.num4会报错

    }
}

三、封装(重要)

①面向对象编程有三大特征:封装,继承,和多态。

1.基本介绍

封装(encapsulation)就是把抽象出来的数据[属性]和对数据的操作(方法)封装在一起。数据被保护在内部,只能通过程序的其他部分提供授权的操作(方法),才能对数据进行操作。

2.好处

①隐藏实现细节。
②可以对数据进行验证,保证安全合理。

3.封装的步骤

①将属性进行私有化
②通过一个公共的set方法,用于对属性进行判断并赋值。语法如下:
public void setXxx(数据类型 参数名称) {
  //加入数据验证业务逻辑
  属性 = 参数名;
}
③通过一个公共的get方法,用来返回属性值。语法如下
public xx getXxx() {
  //权限判断
  return xx;
}

④举一个例子,人(Person)类有三个属性,String类型 名字(name),int类型 年龄(age),double类型 薪水(salary)。且年龄在1-120岁之间,否则赋值18。名字长度在2-6之间,否则赋值无名。代码如下:

class Person {
    private String name;
    private int age;
    private double salary;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 1 && age <= 120) {
            this.age = age;
        } else {
            System.out.println("年龄需要在1-120, 给默认年龄18");
            this.age = 18; // 默认值18岁
        }

    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getName() {

        return name;
    }

    public void setName(String name) {
        if (name.length() >= 2 && name.length() <= 6) {
            this.name = name;
        } else {
            System.out.println("名字长度不对, 默认名字无名");
            this.name = "无名";
        }

    }

    public void info() {
        System.out.println("name = " + name + " age = " + age + "salary = " + salary);
    }
}

4.封装与构造方法

为了防止构造方法创建时跳过验证,可以在构造方法里调用方法。代码如下:

public Person(String name, int age, double salary) {
    setName(name);
    setAge(age);
    setSalary(salary);
}

四、继承(重要)

1.基本介绍

继承可以解决代码复用,让编程更加靠近人类的思维。当多个类存在相同的变量(属性)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所在的子类不需要重新定义这些属性和方法,只需要通过extends继承来声明父类继承即可。

2.示意图

在这里插入图片描述

3.基本语法

class 子类 extends 父类 {
}
Tips:①子类自动拥有父类定义的属性和方法(前提是要权限允许)。
②父类也叫超类,基类。
③子类也叫派生类。

4.继承的好处

1.提升代码复用性。
2.代码的可扩展性和维护性提高。

5.继承的细节

1.子类继承父类的全部属性和方法,但权限不允许的不能在子类访问,要通过公共方法访问。
2.子类必须要调用父类的构造方法 ,完成父类的初始化。代码如下:
Base父类:

public class Base {
    public int num1 = 1;
    protected int num2 = 2;
    int num3 = 3;
    private int num4 = 4;
    public Base(){
        System.out.println("Base()构造方法被调用");
    }
}

Sub子类:

public class Sub extends Base {
    public Sub(){
        System.out.println("Sub()构造方法被调用"); // super()被隐藏
    }
}

调用:

public class ExtendDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();
    }
}

输出结果:

//Base()构造方法被调用
//sSub()构造方法被调用

3.当创建子类对象时,不管使用那个子类的构造方法,默认情况下,总会调用父类的无参构造方法,如果父类没有提供无参构造方法,则必须在子类的构造方法中使用super去指定使用父类的哪个构造方法完成父类的初始化。代码如下:
Base类;

public class Base {
    public int num1 = 1;
    protected int num2 = 2;
    int num3 = 3;
    private int num4 = 4;
    public Base(){
        System.out.println("Base()构造方法被调用");
    }
    public Base(int num){
        System.out.println("Base(int num)构造方法被调用");
    }
}

Sub类:

public class Sub extends Base {
    public Sub(){
        System.out.println("Sub()构造方法被调用"); // super()被隐藏
    }
    
    public Sub(int num){
        System.out.println("Sub(int num))构造方法被调用"); // super()被隐藏
    }
}    

调用:

public class ExtendDetail {
    public static void main(String[] args) {
        Sub sub = new Sub(1);
    }
}

运行结果:

//Base()构造方法被调用
//Sub(int num))构造方法被调用

4.如果希望指定去调用父类的某个构造方法,则是显式的调用一下。super(参数列表)。代码不列出完了。只写出子类的构造方法。如下:

public Sub(){
    super(1)
    System.out.println("Sub()构造方法被调用"); // super()被隐藏
}

5.如果使用super,super需要放在第一行。super只能放在构造器中。
6.super和this都只能放在构造方法的第一行,因此这两个方法不能同时存在在一个构造器。
7.java所有类都是Object类的子类
8.父类构造方法的调用不限于直接父类,将一直往上追溯至Object类(定级父类)。
9.子类最多只能继承一个父类(指直接继承),即Java中是单继承机制。
10.不能滥用继承机制,子类和父类之间必须满足is-a的关系。比如学生是一个人,猫是一个动物。

五、super关键字

1.基本介绍

super代表父类的引用,用于访问父类的属性,方法,构造方法。

2.基本语法

1.super.属性名
访问父类的属性,但不能访问父类的没有权限的属性。
2.super.方法名(参数列表)
访问父类的方法,但不能访问父类的没有权限的方法。
1.super(参数列表)
super(参数列表);只能放在构造方法的第一句,调用父类构造方法。

3.细节

1.调用父类的构造方法的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)。
2.当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super,this,直接访问是一样的效果.
3.super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类;如果多个父类中都有同名的成员,使用super访问遵循就近原则。当然也需要遵循访问权限的相关规则。

4.this与super的区别

区别点thissuper
访问属性访问本类的属性,如果本类没有此属性,则从父类中继续查找访问父类的属性
调用方法访问本类的方法,如果本类没有此方法,则从父类中继续查找访问父类的方法
调用构造方法调用本类的构造方法,必须放在构造方法第一行调用父类的构造方法,必须放在构造方法第一行
特殊表示当前对象表示子类访问父类的对象

六、方法重写(override)

1.基本介绍

方法重写就是子类有一个方法和父类某个方法的名称,返回类型,参数一样。那么我们就说子类的这个方法重写了父类的那个方法。(有些地方重写也叫覆盖)。
举一个例子,代码如下:

package com.hh6879.override_;

public class Override01 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.speak();
    }
}

class Animal {
    public void speak() {
        System.out.println("动物叫");
    }
}

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

输出结果:

//小狗汪汪叫

2.细节

1.子类方法的方法名和参数,要和父类方法的方法名和参数完全一致。
2.子类的方法返回类型和父类返回类型一样,或者父类返回类型的子类。比如父类返回类型是Object,子类返回类型是String。代码如下:
父类方法:

public Object f1(){
	return null;
}

子类方法:

public String f1(){
	return null;
}

3.子类方法不能缩小父类方法的返回权限。

七、多态(重要)

1.基本介绍

方法或对象具有多种形态,是面向对象的第三大特征,多态建立在封装和继承的基础之上的。

2.方法的多态

重载和重写体现方法的多态。

3.对象的多态

1.一个对象的编译类型和运行类型可以不一致。
2.编译类型在定义对象时 就确定了,不能改变。
3.运行类型是可以改变的。
4.编译类型就看定义时 =号左边,运行类型就看定义时 =号右边。代码如下,类的定义就不放出了,Animal是父类,Dog,Cat是子类。

public class PolyObject {
    public static void main(String[] args) {
        Animal animal = new Dog();
        // 编译类型是Animal, 运行对象是Dog。
        animal.speak(); // 找的是Dog的speak()方法
        animal = new Cat();
        // 运行类型变成了Cat, 编译类型依然是Animal
        animal.speak(); // 找的是Cat的speak()方法
    }
}

运行结果:

//Dog 狗在叫...
//Cat 猫在叫...

4.多态向上转型

1.多态的前提是两者存在继承关系
2.多态的向上转型
  1)本质就是父类的引用指向子类的对象
  2)语法:父类类型 引用名 = new 子类类型();
  3)特点:①编译类型看左边,运行类型类型看右边。
②可以调用父类的所有成员(需要遵守访问权限规则)
③不能调用子类的特有成员。因为在编译阶段能调用那些成员是由编译类型决定的。
④最终的运行效果看运行类型的具体实现,即调用成员时,按照运行类型开始查找,规则和上面说的继承规则一样。
代码例子:同上。

5.多态向下转型

1.语法:子类类型 引用名 = (子类类型) 父类引用
特点: 1)只能强转父类的引用,不能强制父类的对象。
2)要求父类的引用必须指向当前目标类型对象。
3)可以调用子类类型中所有成员。

6.属性重写问题

属性没有重写之说,属性的值看编译类型。演示代码如下:

public class PolyDetail03 {
    public static void main(String[] args) {
        Person person = new Student();
        System.out.println(person.age); // 直接看编译类型Person的age
        // 输出结果为: 25
    }
}

class Person {
    int age = 25;
}

class Student extends Person {
    int age =18;
}

7.动态绑定机制

1.当调用方法对象方法时,该方法会和该对象的内存地址(运行类型)绑定。
2.当调用属性时,没有动态绑定机制,哪里声明,哪里使用。即当前类返回当前类的属性。
3。举一个例子,对比说明,代码如下:
第一个:

public class DynamicBinding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum()); // 输出结果为: 4
        System.out.println(a.sum1()); // 输出结果为: 3
    }
}

class A { // 父类
    public int num = 1;

    public int sum() {
        return getNum() + 1;
    }

    public int sum1() {
        return num + 1;
    }

    public int getNum() {
        return num;
    }
}

class B extends A { // 子类
    public int num = 2;

    public int sum() {
        return getNum() + 2;
    }

    public int sum1() {
        return num + 1;
    }

    public int getNum() {
        return num;
    }
}

删掉子类的sum和sum1方法后运行结果如下:

public class DynamicBinding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum()); // 输出结果为: 3
        System.out.println(a.sum1()); // 输出结果为: 2
    }
}

class A { // 父类
    public int num = 1;

    public int sum() {
        return getNum() + 1;
    }

    public int sum1() {
        return num + 1;
    }

    public int getNum() {
        return num;
    }
}

class B extends A { // 子类
    public int num = 2;

    public int getNum() {
        return num;
    }
}

8.补充:

1)多态数组
数组定义类型为父类,里面保存的实际元素类型可以为子类类型。
2)多态参数
发法定义的形参列表类型为父类,实参允许为子类类型。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值