本章技能点列表
面向对象的三大特征
1. 继承 inheritance
• 子类 父类
• 子类可以从父类继承属性和方法
• 子类可以提供自己单独的属性和方法
2. 封装/隐藏encapsulation
• 对外隐藏某些属性和方法
• 对外公开某些属性和方法
3. 多态 polymorphism
• 为了适应需求的多种变化, 使代码变得更加通用!
• 面向过程只有封装性(功能的封装, 而没有数据的封装) , 没有继承和多态
隐藏/封装(encapsulation)
-
为什么需要封装? 封装的作用和含义?
• 我要看电视, 只需要按一下开关和换台就可以了。 有必要了解电视机内部的结构吗? 有必要碰碰显像管吗? -
隐藏对象内部的复杂性, 只对外公开简单的接口。 便于外界调用, 从而提高系统的可扩展性、 可维护性。
-
我们程序设计要追求“高内聚, 低耦合” 。
• 高内聚 : 就是类的内部数据操作细节自己完成, 不允许外部干涉;
• 低耦合 : 仅暴露少量的方法给外部使用
使用访问控制符, 实现封装
-
成员(成员变量或成员方法) 访问权限共有四种:
• public 公共的:可以被项目中所有的类访问。 (项目可见性)
• protected 受保护的:可以被这个类本身访问; 同一个包中的所有其他的类访问; 被它的子类(同一个包以及不同包中的子类) 访问
• default/friendly 默认的/友好的(包可见性):被这个类本身访问; 被同一个包中的类访问。
• private 私有的:只能被这个类本身访问。 (类可见性) -
类的访问权限只有两种:
• public 公共的:可被同一项目中所有的类访问。 (必须与文件名同名)
• default/friendly 默认的/友好的:可被同一个包中的类访问。
- 类的属性的处理:
• 一般使用private. (除非本属性确定会让子类继承)
• 提供相应的get/set方法来访问相关属性。这些方法通常是public, 从而提供对属性的读取操作。
(注意: boolean变量的get方法是用: is开头!)
• 一些只用于本类的辅助性方法可以用private,
• 希望其他类调用的方法用public
package cn.sxt.oo2;
public class Human {
private int age ;
String name; //可以被本包下面的类访问
protected int height;
public void sayAge(){
System.out.println(age);
}
}
package cn.sxt.oo2;
/**
* 测试封装
*
*/
public class TestEncapsulation {
public static void main(String[] args) {
Human h = new Human();
// h.age = 13;
h.name = "高";
h.height = 230;
Person4 p4 = new Person4();
// p4.age = 14;
p4.setAge(-14);
System.out.println(p4.getAge());
}
}
class Boy extends Human {
public void sayHello(){
// System.out.println(age); //子类无法使用父类的私有属性和方法
}
}
package cn.sxt.oo2;
/**
* 仍然测试封装
*
*/
public class Person4 {
private int id;
private String name;
private int age;
private boolean man;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setAge(int age) {
if(age>=1&&age<=130){
this.age = age;
}else{
System.out.println("请输入正常的年龄!");
}
}
public int getAge(){
return this.age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean isMan() {
return man;
}
public void setMan(boolean man) {
this.man = man;
}
}
继承
• 类是对对象的抽象, 继承是对某一批类的抽象, 从而实现对现实世界更好的建模。
• 提高代码的复用性!
• extands的意思是“扩展” 。 子类是父类的扩展
• 不同的叫法: 超类、 父类、 基类、 子类、 派生类
- 通过继承可以简化类的定义, 实现代码的重用
- 子类继承父类的成员变量和成员方法, 但不继承父类的构造方法
- java中只有单继承 , 没有像c++那样的多继承。 多继承会引起混乱, 使得继承链过于复杂, 系统难于维护。 就像我们现实中, 如果你有多个父母亲, 那是一个多么混乱的世界啊。 多继承, 就是为了实现代码的复用性, 却引入了复杂性, 使得系统类之间的关系混乱。
- java中的多继承, 可以通过接口来实现
- 如果定义一个类时, 没有调用extends, 则它的父类是:java.lang.Object
package cn.sxt.oo2;
/**
* 测试继承
*
*/
public class TestExtends {
public static void main(String[] args) {
Student stu = new Student();
stu.name="高";
stu.height = 172;
stu.rest();
Student stu2 = new Student("希希",6,"挖掘机专业");
System.out.println(stu2 instanceof Student);
System.out.println(stu2 instanceof Person );
System.out.println(stu2 instanceof Object );
System.out.println(new Person() instanceof Student );
}
}
class Person /*extends Object*/ {
String name;
int height;
public void rest(){
System.out.println("休息一会!");
}
}
class Student extends Person {
String major;
public void study(){
System.out.println("学习两小时!");
}
public Student(String name,int height, String major){
this.name = name;
this.height = height;
this.major = major;
}
public Student(){
}
}
方法的重写(override)
• 在子类中可以根据需要对从基类中继承来的方法进行重写。
• 重写方法必须和被重写方法具有相同方法名称、 参数列表和返回类型。
•重写方法不能使用比被重写方法更严格的访问权限。 (由于多态)
Object类
• Object类是所有Java类的根基类
• 如果在类的声明中未使用extends关键字指明其基类, 则默认基类为Object类
• 重写: toString方法:
• 默认返回: 包名+类名+@+哈希码
• 可以重写!
• 打开API文档, 开始熟悉!
public class Person {
…
}
public class Person extends Object {
…
}
根据对象内存位置生成, 唯一不重复
package cn.sxt.oo2;
/**
* 测试重写(override)/覆盖
*
*/
public class TestOverride {
public static void main(String[] args) {
Horse h = new Horse();
h.run();
}
}
class Vehicle {
public void run(){
System.out.println("跑....");
}
public void stop(){
System.out.println("停止!");
}
public Person whoIsPsg(){
return new Person();
}
}
class Horse extends Vehicle {
public void run(){
System.out.println("四蹄翻飞,嘚嘚的。。。");
}
public Student whoIsPsg(){//返回值类型小于等于父类的类型
return new Student();
}
}
super关键字
• super是直接父类对象的引用。
• 可以通过super来访问父类中被子类覆盖的方法或属性。
package cn.sxt.oo2;
public class TestSuper01 {
public static void main(String[] args) {
new ChildClass().f();
}
}
class FatherClass {
public int value;
public void f(){
value = 100;
System.out.println ("FatherClass.value="+value);
}
}
class ChildClass extends FatherClass {
public int value;
public void f() {
super.f(); //调用父类对象的普通方法
value = 200;
System.out.println("ChildClass.value="+value);
System.out.println(value);
System.out.println(super.value); //调用父类对象的成员变量
}
}
package cn.sxt.oo2;
public class TestSuper02 {
public static void main(String[] args) {
System.out.println("开始创建一个ChildClass对象......");
new ChildClass2();
}
}
class FatherClass2 {
public FatherClass2() {
super();
System.out.println("创建FatherClass");
}
}
class ChildClass2 extends FatherClass2 {
public ChildClass2() {
super();
System.out.println("创建ChildClass");
}
}
继承深化
-
父类方法的重写:
• “==”: 方法名、 形参列表相同。
• “≤≤” : 返回值类型和异常类型, 子类小于等于父类。
• “≥” : 访问权限, 子类大于等于父类 -
构造方法调用顺序:
• 根据super的说明, 构造方法第一句 总是: super(…)来调用父类对应的构造方法。
• 先向上追溯到Object, 然后再依次向下执行类的初始化块和构造方法, 直到当前子类为止。
对象的比较— 和equals()
3. :
• 比较两基本类型变量的值是否相等
• 比较两个引用类型的值即内存地址是否相等, 即是否指向同一对象。
4. equals() :
• 两对象的内容是否一致
5. 示例
• object1.equals(object2) 如: p1.equals(p2)
• 比较所指对象的内容是否一样,是比较两个对象, 而非两个基本数据类型的变量
• object1 == object2 如: p1==p2
• 比较p1和p2的值即 内存地址是否相等, 即是否是指向同一对象。
6. 自定义类须重写equals(), 否则其对象比较结果总是false。
package cn.sxt.oo2;
public class TestEquals {
public static void main(String[] args) {
Object obj;
String str;
User u1 = new User(1000,"高淇","123456");
User u2 = new User(1000,"高希希","123456");
System.out.println(u1==u2);
System.out.println(u1.equals(u2));
String str1 = new String("sxt");
String str2 = new String("sxt");
System.out.println(str1==str2); //false
System.out.println(str1.equals(str2)); //true
}
}
class User {
int id;
String name;
String pwd;
public User(int id, String name, String pwd) {
super();
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id != other.id)
return false;
return true;
}
}
多态 polymorphism
• 多态性是OOP中的一个重要特性, 主要是用来实现动态联编的, 换句话说, 就是程序的最终状态只有在执行过程中才被决定而非在编译期间就决定了。 这对于大型系统来说能提高系统的灵活性和扩展性。
• java中如何实现多态?使用多态的好处?
• 引用变量的两种类型:
• 编译时类型(模糊一点, 一般是一个父类)
• 由声明时的类型决定。
• 运行时类型(运行时, 具体是哪个子类就是哪个子类)
• 由实际对应的对象类型决定。
• 多态的存在要有3个必要条件:
• 要有继承, 要有方法重写, 父类引用指向子类对象
引用数据类型的类型转换
• 子类转换为父类: 自动转换
• 上转型对象不能操作子类新增的成员变量和方法。
• 上转型对象可以操作子类继承或重写的成员变量和方法
• 如果子类重写了父类的某个方法, 上转型对象调用该方法时, 是调用的重写方法。
• 父类转换为子类: 强制转换
• (绝不是做手术, 而是父类的真面目就是一个子类, 否则会出现类型转换错误)
final关键字
• final可以用来修饰变量, 方法, 类。
- 修饰变量: 变量一旦被初始化便不可改变, 相当定义了一常量。
• final int x=3;
• x=4; - 修饰方法: final方法是在子类中不能被覆盖的方法
• final returnType methodName(paramList){…}
• final void eat() { … } - 修饰类: final类是无法被任何类继承的。
• final class finalClassName{ … }
• final class Person{ … }