java基础学习总结(七):面向对象

面向对象

面向对象(Object Oriented,OO)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术 [1] 发展到一定阶段后的产物。

  • 面向过程:在整个事情的执行过程中,自己至始至终都是其中的参与者,并且要自己亲力亲为所有的步骤。
  • 面向对象:我们需要完成某个事情,需要具体的一个结果。这时我们不会自己去参与其中,而是找具备这些功能的对象,然后调用它们的功能,帮助我们来完成我们的要求。(面向对象只关注结果,不关注过程)

1. 对象在代码中的体现

创建一个对象

/**
 * 这是一个对象:人
 */
public class Person {
    // 对象的属性
    private String name;
    private int age;
    private char sex;
    // 对象的方法(功能)
        public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    void eat(){
        System.out.println("吃东西");
    }
    void run(){
        System.out.println("跑步");
    }
}

使用一个对象

public class TestPerson {
    public static void main(String[] args) {
        // 创建对象
        Person2 person2 = new Person2();
        // 调用对象给属性赋值
        person2.setName("jack");
        // 用对象调用方法
        person2.run();
    }
}

2. 对象的内存图解

java基础学习总结(七):面向对象_第1张图片

3. 堆中是否有垃圾?

8. public static void main(String[] args) {
9.		//调用其他类的方法或属性赋值,必须通过对象
10.		Student s = new Student();
11.		//赋值
12.		s.name = "李四";
13.		s.age = 18;
14.		//调用方法
15.		s.study();
16.		Student s1 = s;
17.		s = null;
18.	}

4. 成员变量和局部变量

局部变量:定义在函数(方法)中的那些变量。局部变量只在定义它的方法(函数)中有效。
成员变量:定义在类的成员位置上的变量。成员变量在整个类中都有效(全局变量是成员变量的俗称);成员变量又分为 实例(对象)变量 和 类变量(static静态变量)。
注意:在类中和该类的一个函数中,同时存在一个相同类型相同名称的变量,在函数被执行时,函数中优先使用定义在函数中的变量(局部变量),符合就近原则。
局部变量和成员变量的区别
1、从定义上来讲:
局部变量定义在函数中。
成员变量定义在类中。
2、从内存存储上来讲:
局部变量随着函数的运行会在栈内存中出现,局部变量存储在栈内存中。
成员变量会随着对象的出现在堆中存在,成员变量存储在堆内存中。
3、从初始值上来讲:
局部变量在定义时需要指定初始值(局部变量没有默认值),只有初始化之后才能使用。
成员变量可以不用初始化,有默认值。
4、从存活时间上来讲(生命周期)
局部变量是随着函数的进栈在函数所属的栈内存中存在,随着函数的出栈就消失。
成员变量是随着对象的出现在堆中出现。随着对象的消失而消失。

5. 封装

封装(Encapsulation)是面向对象方法的重要原则,就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。(就是把过程和数据包装起来,对数据的访问只能通过已定义的接口)

  • 如何封装:通过private关键字
  1. 定义类的时候,类的属性都需要使用private进行修饰的
  2. 被private修饰后,必须创建成员方法(setXXX和getXXX)
  3. 通过对象,调用(setXXX和getXXX)来间接操作被private修饰的属性

6. this关键字

this: 代表所在类的对象引用,方法被哪个对象调用,在方法中就会有一个隐式的变量this记录着调用对象的地址。
① 在类中,如果要表示成员变量,就使用this.变量名来表示;
② 当在函数中局部变量和成员变量同名的时候,在函数中如果要访问成员变量,这时需要使用this来区分;
③ 如果在函数中没有和成员变量同名的局部变量名时,在函数中可以省略this;
④ 在函数中,如果局部变量和成员变量名字和类型都一致,那么函数会先使用局部变量;

7. 构造函数

  • 构造函数:在创建对象时(使用new时),会自动调用的函数。
  • 定义格式
    修饰符 构造函数名( 参数列表 ){

}

  • 构造函数定义的特点
    1、构造函数是用来创建对象的,它不需要书写返回值类型。
    2、构造函数的名字要求必须和当前的类名一致。因此构造函数的名称书写和类名一致。
    3、参数列表,可以和一般函数的参数列表一样。
  • 构造函数的作用:就是为了给对象初始化时使用的。(就是为了给对象成员变量初始化值使用的)。
  • 默认构造函数:就是没有参数的构造函数
    当我们书写一个类的时候,在这个类中如果不写任何的构造函数,那么在使用javac编译完这个java源代码之后,生成的class文件中,会自动的添加一个没有参数的构造函数。
    注意:如果我们在写一个类的时候,手动的书写了有参数的或者无参数的构造函数,那么在使用javac启动编译器对Java源代码进行编译的时候,编译器就不会在class文件中添加这个默认的构造函数了。
  • 构造函数和一般函数区别
  1. 它们的执行时间不同:
    构造函数是在创建对象的过程中执行。当对象创建完成了,构造函数就已经执行结束。
    一般函数执行时间有两种情况:
    ① 如果调用其他类中的函数时:一般函数通过是在对象创建完成之后,通过对象的引用来调用。
    ② 如果调用本类中的函数时:什么时候使用,什么时候调用。
  2. 它们的调用次数不同:
    构造函数只有在new对象的时候,会被调用,一旦对象创建完成,我们不能手动的人为去调用构造函数。
    一般函数可以通过对象随意的调用,没有次数限制。
  3. 它们互相调用问题:
    在构造函数中可以去调用一般的函数,但是在一般的函数中不能调用构造函数。
  • 构造函数中调用其他构造函数
  1. 构造函数之间不能相互嵌套调用,这样就会导致无限制的调用构造函数,导致永远无法停止调用。
  2. this调用构造函数,必须放在构造函数中的第一句。我们通过this调用构造函数的目的是希望通过其他的构造函数完成初始化动作,因此要求其他构造函数的初始化必须在本构造函数中语句执行之前先初始化完成。
    // this调用其它构造函数的格式:  this(参数列表);  // 相当于 构造函数(参数列表)
    public Person(String name){
        this.name = name;
    }
    public Person(String name, int age) {
        this("jack");
        this.name = name;
        this.age = age;
    }

8. 继承

  • 继承的概念:子类无条件拥有父类中所有可继承的属性和行为。(构造方法不可继承,因此构造方法只能创建当前类对象)
  • 继承的格式:继承使用“extends”关键字实现
    class 子类 extends 父类 {
    }
  • 继承的作用:提高代码的可维护性和复用性,使代码更加简洁。
  • 继承的类型
    ① 单继承:类B继承类A
    ② 多重继承:类C继承类B,类B继承类A
    ③ 不同类继承同个类:类B继承类A,类C继承类A
    ④ 不支持多继承:类C继承类A和类B
  • 继承的特性
    ①子类拥有父类非 private 的属性、方法。
    ② 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
    ③ 子类可以用自己的方式实现父类的方法。
    ④ Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
    ⑤ 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

9. 方法重写

子类重写父类中继承而来的方法
注意点:
① 方法名必须与父类中继承而来的方法名称一模一样.
② 方法的参数列表也要保持一致.
③ 方法的返回值也要相同.
④ 子类重写的方法访问权限修饰符必须要 大于等于 父类中重写的方法.
⑤ 重写方法不能抛出比被重写方法申明更加宽泛的异常.

10. super关键字

我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

public class Son extends Father {
	private String knowledge;		
	public Son(String name, int age, char gender, int money, String knowledge) {
		// 说明 : 将父类中继承而来的属性, 传递给父类进行初始化.
		super(name, age, gender, money);
		// 说明 : 自己的属性自己初始化.
		this.knowledge = knowledge;
	}	
	public Son() {
		// 构造方法的调用在构造方法中, 必须是第一条执行语句.
		super();		
		System.out.println("调用了 Son() 无参构造方法...");
	}
	@Override
	public void introduce() {
	    // 调用父类中的 introduce() 方法
		super.introduce();
		System.out.println("我的知识是 : " + knowledge);	
	}
}

11. 多态

  • 什么是多态
    父类的引用指向了子类的对象,就是同一个行为具有多个不同表现形式或形态的能力,就是同一个接口,使用不同的实例而执行不同操作。
  • 多态的优点:① 消除类型之间的耦合关系;② 可替换性;③ 可扩充性;④ 接口性;⑤ 灵活性;⑥ 简化性
  • 多态存在的三个必要条件:① 继承;② 重写;③ 父类引用指向子类对象
  • 多态的使用场景:
    ① 方法的参数设计
	public static void main(String[] args) {
		Actress a = new Actress();
		a.setName("女演员");
		Doctor d = new Doctor();
		d.setName("女医生");
		cut(b);
	}
	// 多态的第一种使用场景 : 在设计一个方法参数时, 尽量将参数设计为 `父类引用`, 因为父类引用可以接收所有的子类对象.
	public static void cut(Person p) {
		p.doSomething();
	}

② 对象引用接收

	public static void main(String[] args) {
		// 多态的第二种使用场景 : 创建子类对象, 使用父类引用接收, 可以提高程序的维护性.
		Person p = new Actress();
		p.setName("女演员");
		cut(p);
	}
  • 多态的弊端:子类特有方法没有在 父类中被定义. 导致父类调用不到子类特有方法
public static void main(String[] args) {	
		// 多态 : 父类引用指向了子类对象.		(向上转型 )
		Person p = new Actress();
		p.setName("女演员");	
		cut(p);
	}
	public static void cut(Person p) {
		p.doSomething();
		// 特有行为 :
		// 报错 : beFamouse() 方法没有在 Person 类中被定义.  因为 p 调用是 Person 类型的.
		// 说明 : 编译器是根据 `对象` 的接收类型类寻找 `属性和方法` 的.
		// 解决方法 : 对象类型 `向下转型`.  将父类引用转换为子类引用.
		Actress a = (Actress) p;
		a.beFamous();
	}

注意:创建的对象与向下转型类型没有保持一致,编译时正常,运行时会报ClassCastException 类型转换异常

  • 关键字instanceof
    作用:判断对象的具体类型
    格式 : 对象 instanceof 类型
    就是判断左边对象是否为右边类型的对象
	public static void main(String[] args) {
		Person p = new Doctor();
		p.setName("女医生");
		cut(p);
	}
	public static void cut(Person p) {
		p.doSomething();					
		// 向下转型的目的 : 为了调用子类特有的 `属性/方法`.
		if (p instanceof Doctor) {			
			Doctor d = (Doctor) p;
			d.temptationOfUniforms();
		} else if (p instanceof Actress) {
			Actress a = (Actress) p;
			a.beFamous();
		} else if (p instanceof Barber) {
			Barber b = (Barber) p;
			b.chasingGirls();
		}
	}
  • 多态的实现方式:① 接口;② 重写;③ 抽象类和抽象方法

12. final关键字

final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写;

  • 声明类:
final class 类名 {
     // 类体		
}
  • 声明方法:
修饰符(public/private/default/protected) final 返回值类型 方法名(){
     // 方法体
}

**注意:**实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final

// 不可更改的属性就应该使用 final 修饰     (public static final 组合关系)
public static final double PI = 3.14;

13. 抽象类

① 如果一个类拥有抽象方法, 那么该类就必须被定义为 抽象类,使用 abstract 关键字修饰.
② 抽象类不能实例化对象,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
③ 抽象类必须被继承,才能被使用
④ Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。

// 抽象类
public abstract class Animal {
	private String name;
	// 抽象方法
	public abstract void shout();
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
  • 抽象方法:没有方法体的方法必须定义为 抽象方法. 使用 abstract 关键字修饰
    定义抽象方法的目的:给所有子类定义行为规范。
    任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
public abstract void eat();
  • 抽象类总结规定
  1. 抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  3. 抽象类中的抽象方法只是声明,不包含方法体。
  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
  • abstract 关键字的补充
    abstract 关键字不能与private,static,final这些关键字共同使用。
    ① private 私有化, 子类都看不见抽象方法, 如何重写
    ② static 关键字修饰的属性和方法, 可以直接使用类名调用,语法错误
    ③ final 表示最终化, 与 abstract 关键字的含义冲突

14. 接口

① 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
② 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
③ 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

  • 接口与类相似点:
    ① 一个接口可以有多个方法。
    ② 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
    ③ 接口的字节码文件保存在 .class 结尾的文件中。
    ④ 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
  • 接口与类的区别:
    ① 接口不能用于实例化对象。
    ② 接口没有构造方法。
    ③ 接口中所有的方法必须是抽象方法。
    ④ 接口不能包含成员变量,除了 static 和 final 变量。
    ⑤ 接口不是被类继承了,而是要被类实现。
    ⑥ 接口支持多继承。
  • 接口的特性
    ① 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
    ② 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
    ③ 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
    ④ 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
    ⑤ 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
    ⑥ 接口中的方法都是公有的
  • 抽象类和接口的区别
    ① 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
    ② 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    ③ 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
    ④ 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
  • 接口和类的关系总结
    ① 接口和接口 : 继承的关系, 使用 extends 实现, 可以多继承.
    ② 接口和类 : 实现的关系, 使用 implements, 可以多实现.
    ③ 类和类 : 继承的关系, 使用 extends 实现, 只能单继承, 但可以完成多重继承.
  • 接口的好处:
    ① 接口可以完成类与类之间的解耦. (程序的低耦合设计)
    ② 接口可以给一个类提供额外的扩展功能.
    ③ 一方(Computer)使用规则, 另一方(Mouse,Keyboard,CameraVideo)实现规则, 此时, 双方就可以完成通信.
  • 代码体现
public interface DrugDetectable {
	// 常量		(默认修饰符 : public static final)
	int YEARS = 5;	
	// 抽象方法	(默认修饰符 : public abstract)
	void drugDetection();
}
// 多实现
public class Dog extends Animal implements BlindGuidable, DrugDetectable {}
// 多继承
public interface Hockey extends Sports, Event{}

15. 最后来个面向接口编程案例

准备一台电脑

public class Computer {
    public void run(){
        System.out.println("run...");
    }
    public void useUSB(USB usb){
        usb.open();
        usb.close();
    }
}

准备一个USB接口

public interface USB {
    void open();
    void close();
}

外接设备实现USB接口

public class Keyboard implements USB{
    @Override
    public void open() {
        System.out.println("Keyboard open");
    }
    @Override
    public void close() {
        System.out.println("Keyboard close");
    }
}
public class Mouse implements USB{
    @Override
    public void open() {
        System.out.println("Mouse open");
    }
    @Override
    public void close() {
        System.out.println("Mouse close");
    }
}

测试

public class TestComputer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.run();
        USB usb = new Mouse();
        computer.useUSB(usb);
        USB usb1 = new Keyboard();
        computer.useUSB(usb1);
    }
}

你可能感兴趣的:(java基础)