一、面向对象(oo)
物以类聚,最终的底层是面向过程;
本质:以类的方式组织代码,以对象的组织(封装)数据;
【面向对象的核心思想】
- 抽象:把共同点抽取出来成为类;
- 三大特性:封装 、继承、 多态;
【类与对象】
-
类是对象的模板;
-
类中一般有属性和方法;
-
构造方法:
- [修饰符] 类名([参数])
- 每一个类默认都会有一个无参构造方法,当你写了一个有参构造方法的时候,这个默认的无参构造方法就会消失;
- 定义了一个无参构造的时候,还想用无参构造方法,需要自己定义;
- void也是返回值,但是构造方法没有返回值;
- 私有构造方法,不能调用,不能实例化;
- 同个类下的不同构造方法实际上就是方法的重载;与返回值无关;
- 作用:初始化值,new本质就是调用构造器;
对象是通过引用来操作的;栈 → 堆;
二、封装
该露的露,该藏的藏,高内聚,低耦合;
【体现】
- 属性私有:get/set 来获取/设置属性;
- 构造私有:单例模式;
- 方法私有;
- 如果不希望类在包外被调用,可以将类设置为缺省的;
【作用】
- 提高程序的安全性,保护数据;
- 隐藏代码的实现细节;
- 统一接口;
- 系统可维护;
【权限修饰符】
通过对不同的方法属性设置不同的权限修饰符来达到对类进行封装的目的;
- 权限从小到大顺序为:private < 缺省 < protected < public
- 具体的修饰范围:
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
【说明】
- 4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类;
- 修饰类,只能使用:缺省、public;
【代码示例】
public class Demo03 {
//private私有化,封装数据
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
//控制私有属性的数据
public void setAge(int age) {
if (age>150 || age<0){
this.age = 3;
}else{
this.age = age;
}
}
}
public static void main(String[] args) {
Demo03 demo03 = new Demo03();
//设置数据
demo03.setAge(50);
//获取数据
System.out.println(demo03.getAge());
}
三、继承
1、继承
【好处】
-
减少了代码的冗余,提高了代码的复用性;
-
便于功能的扩展;
-
为之后多态性的使用,提供了前提;
【格式】
class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
【说明】
-
继承的本质是对某一批类的抽象(父类);
-
extends 的意思是“扩展”,子类(派生类)是父类的扩展;
-
java只有单继承,一个类只能继承一个父类;一个父类可以被多个子类继承;
【super】
-
super调用父类的构造方法,必须在构造方法的第一行;
-
super只能出现在子类的方法或者构造方法中;
-
super和this不能同时调用构造方法;
-
子类的构造方法会默认调用父类的无参构造方法,super();
【super 与 this】
- 代表的对象不同:
- this:本身调用这个对象;
- super:代表父类对象的引用;
- 前提:
- this:没有继承也可以是使用;
- super:只能在继承条件才可以使用;
- 构造方法:
-
this() ;本类的构造;
-
super();父类的构造;
-
2、Object类
2.1 java.lang.Object
java.lang.Object是Java中所有类的父类,类似于二叉树中的根节点,定义了一些通用的方法;
【说明】
- 如果我们没显式的声明一个类的父类的话,则此类继承于java.lang.Object类;
- 所的java类(除java.lang.Object类之外都直接或间接的继承于java.lang.Object类;
- 意味着,所的java类具有java.lang.Object类声明的功能;
【常用方法】
方法名 | 类型 | 描述 |
---|---|---|
public Object() | 构造方法 | 构造器 |
public boolean equals( Object obj) | 普通方法 | 对象比较 |
public int hashCode() | 普通方法 | 获取Hash码 |
public String toString() | 普通方法 | 对象打印时调用 |
【注意】
- 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类;
- 数组也作为 Object类的子类出现,可以调用 Object类中声明的方法;
- Object类只声明了一个空参的构造器;
2.2 equals()方法
是一个方法,而非运算符;
只能适用于引用数据类型;
【定义】
public boolean equals(Object obj) {
return (this == obj);
}
【说明】
- Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体;
- 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同;
- 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写;
- 重写的原则:比较两个对象的实体内容是否相同;
【== 运算符】
-
可以使用在基本数据类型变量和引用数据类型变量中
-
如果比较的是基本数据类型变量:
比较两个变量保存的数据是否相等。(不一定类型要相同);
-
如果比较的是引用数据类型变量:
比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体;
【注意】
- == 符号使用时,必须保证符号左右两边的变量类型一致;
- 基本数据类型用==,引用数据类型用equals;
2.3 toString()方法
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()方法;
【定义】
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
【说明】
- 像String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回"实体内容"信息;
- 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容";
3、子类对象的实例化
【结果:继承性】
子类继承父类以后,就获取了父类中声明的属性或方法。 创建子类的对象,在堆空间中,就会加载所父类中声明的属性;
【过程】
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用;
【注意】
- 子类继承父类以后,仍然认为获取了父类中私的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已;
- 虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象;
四、多态
可以理解为一个事物的多种形态;
4.1 多态的初步认识
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用);编译时和运行时类型不一致,产生了多态;
【代码示例】
//一个动物父类,两个子类重写了父类的shout()方法;
public class TestAnimal{
public static void main(String[] args) {
Animal animal = new animal();
animal.shout();
animal = new Dog();
animal.shout();
w = new Cat();
animal.shout();
}
}
//结果:
//动物叫
//狗子叫
//猫猫叫
【说明】
-
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法;
-
对象的多态:在Java中,子类的对象可以替代父类的对象使用;
- 一个变量只能有一种确定的数据类型;
- 一个引用类型变量可能指向多种不同类型的对象;
【多态前提】
- 类的继承(实现)关系;
- 方法的重写;
- 向上转型(父类的引用指向子类对象);
【注意】
-
继承链中对象方法的调用的优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O);
-
多态是运行时行为;
-
若子类重写了父类方法,就意味着子类里面定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边;
-
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边;
【多态是方法的多态,属性没有多态】
4.2 instanceof
【说明】
- a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false;
- 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类;
- 要求a所属的类与类A必须是子类和父类的关系,否则编译错误;
Student student = new Student();
Object object = new Student();
System.out.println(student instanceof Person);
System.out.println(person instanceof Object);
System.out.println(object instanceof Person);
System.out.println(object instanceof String);
//输出
true
true
true
false
4.3 向上向下转型
在有继承关系的类,向上是转为父类的引用类型;向下则相反;
【向上原因】多态
【向下原因】
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用;
使用向下转型才能调用子类特的属性和方法;
【如何向下转型】
使用强制类型转换符:()
Person p = new Man();
Man m1=(Man)p2;//向下转型
【代码例子】
Student student = new Student();
Object object = new Student();
//向上转型, 可能会丢失子类的一些方法
Person person = student;
System.out.println(person.name); //父类
//向下转型,强制转换
Student student1 = (Student) person;
System.out.println(student1.name); //子类
【注意】
- 使用强转时,可能出现ClassCastException的异常;
- 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。;
- 只有对象A是B的子类实例化对象或者在下层的子类,才能向下转型;