面向对象的三大特性
封装 继承 多态
面向对象
-
对象和类的关系
类是对象的抽象,对象是类的具体
类是模板,对象是实例
1、封装
-
概念及原因
-
概念:尽可能的隐藏对象的内部实现细节,控制对象的修饰及访问权限将类中。
尽可能多的信息隐藏起来,不让外部类 直接访问,而是提供公开的get/set方法来访问类中的信息
-
-
封装的步骤
- 首先将属性私有化 ,即将访问修饰符修改为private
- 其次为这些私有属性提供相应的get() set() 方法
private String name ;
private int idCode;
// 两对get() set() 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIdCode() {
return idCode;
}
public void setIdCode(int idCode) {
this.idCode = idCode;
}
-
封装的优点
1.便于使用者正确使用系统,防止错误修改属性
2.降低了构建大型系统的风险
3.提高程序的可重用性
4.降低程序之间的耦合度
2、继承
-
概念 : 关键字extends 连接 ,完成类与类之间的方法及属性的给予。形式:子类 extends 父类
Java中的继承为单跟继承,只有一个直接父类,而间接父类则可以有很多个
-
super关键字
- super关键字的含义 : 父类对象
- super关键字的适用场景
- 子类构造方法之中引用父类的构造方法,只能引用一个(无参或者有参的构造方法),并且放在首行,与this只能有一个,二者不可兼容。如果不写,则隐性的默认为super(),即无参构造。
- 子类之中引用父类的方法以及属性 ,使用super.属性名 或者 super.方法
-
子类不能继承的
- 父类的构造方法
- 父类用私有访问修饰符(private)修饰的属性及方法
- 用default(通常不写,默认的就是他)修饰的属性以及方法,不同包下不可继承
-
继承关系下的对象创建
在创建子类对象时优先创建父类对象,在子类构造方法首行有默认的super();
对象创建流程:首先创建父类对象,然后执行属性的初始化,再执行构造方法中的逻辑代码。
3、重写
-
要求
- 父子继承的关系
- 方法名,参数列表一致
- 返回值类型与父类的返回值一致或者是父类返回值的子类
- 访问修饰符不能严于父类的访问修饰符
- 不能抛出比父类更多的异常
-
为什么重写
因为父类提供的方法无法满足子类的需求时,可以在子类中定义和父类相同的方法进行覆盖,增强代码的重用性和复用性。
-
重写之后的执行机制、
子类覆盖父类方法后,优先执行子类覆盖后的方法版本。
-
Object的几个经常重写的常用方法
-
toString——————(public String toString(){})
-
常规的Object的toString方法是打印 包名+类名+@+十六进制的hash码
-
通常在打印对象名的时候,会默认的调用该对象的toString方法
-
而我们重写的时候,常用的是将一个对象的所有的属性打印出来
-
-
equals———————(public boolean equals(){})
-
有一个非常重要的知识点:== 与 equals的区别
== 比较基本数据类型的时候,比较的是值。比较引用类型的时候,比较的是地址。
equals本质上也是 == 而String类中,对equals进行了重写,将两个字符串分别转换成了两个字符数组,然后再循环遍历数组的每一个元素进行比较。因此,String中的equals可以比较字符串的内容
-
-
hashCode——————(public int hashCode(){})
-
hashCode方法返回的是对象对地址的表现形式,十进制,是有hash算法算出来的
-
为什么需要重写hashCode
因为在我们的日常开发 或者 很多数据结构中 有一个要求:两个对象如果使用equals比较为true 那么它们的hash值必须一样,所以我们重写equals方法的同时,都会重写hashCode
-
关于hashCode的附加注释
有两点需要注意的就是:权重prime以及result
权重prime用31 因为31比较特殊,它和所有的数相乘都等于该数乘以2的五次方减去本身,即(a << 5 - a 。 << 左移 几位 表示 乘以2 的几方 , 右移几位 表示除以2的几次方)
result用1
-
public class Worker { private String name ; private int idCode; // 两对get() set() 方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIdCode() { return idCode; } public void setIdCode(int idCode) { this.idCode = idCode; } // 构造方法 public Worker() {} public Worker(String name , int idCode) { this.name = name ; this.idCode = idCode ; } //重写toString @Override public String toString() { return "Worker[name: " + name + ",idCode : " + idCode + "]"; } // 重写 equals @Override public boolean equals(Object obj) { if(this == obj ) { return true ; } if(obj instanceof Worker) { Worker w = (Worker)obj ; if(this.name.equals(w.getName()) && this.idCode == w.idCode) { return true ; } } return false ; } //重写hashCode @Override public int hashCode() { final int prime = 31 ; // 权重 int result = 1 ; // 结果 result = prime* result + idCode ; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result ; } }
-
4、多态
-
概念以及作用
- 概念:父类引用指向子类对象,从而产生多种形态 。也可以理解为,执行同一个操作,由于环境的不同,而实现不同的效果。
- 作用:
- 提高代码的重用性
- 提高代码的可维护性,扩展性
-
适用场景
- 父类引用作为方法的形参,实现多态。使方法的参数类型范围更加宽泛(他的所有子类都能作为形参)。
- 父类引用作为方法的返回值,实现多态。使方法的返回值范围更加宽泛(可返回所有的子类)
-
向上转型(装箱)
- 父类引用指向子类,父类引用保存真实的子类对象。
- 上转型对象,可以调用父类的属性,方法,以及子类重写之后的子类方法。但是对于子类独有的属性及方法,则不能调用。
-
向下转型(拆箱)
-
将父类引用中的真实子类对象,强转回子类本身的类型
-
父类引用的子类对象只有向下转型之后,才能调用子类自己本身的独有属性及方法。
-
向下转型的时候,为了不出现类型不匹配的问题,需要进行判断引用中的子类的真实类型。
用 instanceof 进行判断
-
-
instanceof关键字
- instanceof 运算符是用来在运行时判断对象是否是指定类及其父类的一个实例。比较的是对象,不能比较基本类型、
- instanceof 使用的格式:上转型的对象 instanceof 子类类型
- instanceof 判断结果为 Boolean类型
5、访问修饰符
类访问修饰符
public (公开) 同一项目
default(默认不写) 同包子类
类成员访问修饰符
- private 私有的 本类有效
- default(默认存在的,不能写)父子类之间不能跨包 只有在8.0之后的版本的接口中写。
- protected 父子类之间
- public 本项目可见
本类 | 同包 | 非同包子类 | 其他 | |
---|---|---|---|---|
private | √ | × | × | × |
default(默认不写) | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
6、static关键字
修饰属性:静态变量
- 内存之中只有一份,任何对象的修改,都会造成改变。
- 在本类之中,任何方法可以直接访问,而在不同的类中的时候,可以通过类名加属性名的访问方式进行访问。
修饰方法:静态方法
I. 静态方法允许直接访问静态成员。
II. 静态方法不能直接访问非静态成员。
III. 静态方法中不允许使用this或super关键字。
IV. 静态方法可以继承,不能覆盖,没有多态。
修饰代码块:静态代码块
- static 修饰的代码块。
- 触发:静态属性和静态代码块的执行 - (仅1次)
顺序:静态属性初始化之后执行静态代码块
作用:可为静态属性赋值,或必要的初始化行为
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("com.question.t1.Dog");
System.out.println("++++++++");
Animal dog = new Dog();
}
}
public class Dog extends Animal{
static int age = 10 ;
static {
System.out.println("=======");
mv();
System.out.println(age);
System.out.println("子类的静态代码块");
}
public static void mv() {
System.out.println("子类的静态方法" + age);
}
public Dog() {
System.out.println("子类的构造方法" + age);
}
}
public class Animal{
static String name = "dahuang" ;
static {
System.out.println(name);
System.out.println("父类静态代码块");
}
public static void ma() {
System.out.println("父类静态方法");
}
public Animal() {
System.out.println("父类的构造方法" + name);
}
}
//执行的结果
//dahuang
//父类静态代码块
//=======
//子类的静态方法10
//10
//子类的静态代码块
//++++++++
//父类的构造方法dahuang
//子类的构造方法10
//结果显示:静态代码块以及属性的初始化在对象的构建之前,类加载时就完成了静态的初始化,而静态代码块之前属性就已经完成了初始化的操作。并且父类的加载在子类之前。