面向过程和面向对象,二者都是一种思想。
- 面向过程(Procedure Oriented Programming):强调的是功能行为,考虑怎么做(以函数为最小单位)。
- 面向对象(Object Oriented Programming):将功能封装进对象,强调具备了功能的对象,考虑谁来做(以类/对象为最小单位)。
我们从面向过程的执行者转化成了面向对象的指挥者。
例如:洗衣服
- 面向过程:浸泡衣服 -> 擦肥皂 -> 刷揉水洗 -> 拧干 这一系列的步骤完成洗衣服,一步一步执行,面向每个步骤,这就是面向过程。
- 面向对象:把衣服放进洗衣机,直接执行洗涤功能,完成洗衣服,洗衣机就是一个实体对象,洗涤就是这个对象具备的功能,只要面向某一对象并执行它具备的功能,不需要知道该功能的具体实现,这就是面向对象。
面向对象的三大特征:封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)。
声明对象
声明格式:
变量类型 变量名 = new 构造函数;
说明:
- 构造函数名与类名相同。
- 变量名是对象名。
- 变量类型是类类型(类名)。
创建对象也称为实例化对象(简称:实例),实例化后可以通过对象名访问该类非私有或非静态的成员变量和成员方法。
示例:
public class Person {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
public class Demo {
public static void main(String[] args) {
//Person无参构造没有显试声明(会被有参构造覆盖),不能创建对象
Person person = new Person("张三", 18);
System.out.println(person.getName()); //张三
System.out.println(person.getAge()); //18
}
}
匿名对象
声明格式:
new 构造函数;
说明:
- 匿名对象没有变量名和变量类型。
- 匿名对象创建后只能使用一次,下次使用需要重新创建。
对于对象使用一次的可以使用匿名对象,否则不推荐,因为频繁创建占堆内存。
示例:
public class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
public class Demo {
public static void main(String[] args) {
//通过有参构造声明创建对象,因为Person无参构造没有显试声明(会被覆盖)
System.out.println(new Person("张三").getName()); //张三
System.out.println(new Person("张三").getAge()); //18
}
}
面向对象的三大特性
封装
程序设计追求:“高内聚,低耦合”。
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
- 低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装性的设计思想。
Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
- 隐藏一个类中不需要对外提供的实现细节。
- 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作。
- 便于修改,增强代码的可维护性。
示例:
public class Goods {
private int size;
private boole hotSell;
Goods(int size) {
this.size = size;
}
public boole getHotSell() {
return size > 1000;
}
}
public class Demo {
public static void main(String[] args){
//隐藏了Goods的对外属性(成员变量),提供了公共的getHotSell()方法,方法内部进行判断处理
System.out.println(new Goods(100).getHotSell());
}
}
继承
Java中继承的关键字是extends。
声明格式:
[public] class 子类类名 extends 父类类名
子类不是父类的子集,而是父类的扩展,子类也称为派生类,父类也称为基类或超类(可以理解为:子类 is a 父类)。
继承规则:
- 子类不能直接访问父类私有的(private)的成员变量和方法。
- Java只支持单继承和多层继承(例:A extends B,B extends C),不允许多继承(例:A extends B,C)。
- 被static修饰的变量和方法不能被继承和重写(因为static修饰的所属于类,非static修饰的所属于对象)
子类继承了父类,就继承了父类的方法和属性,在子类中,可以使用父类中定义的方法和属性,也可以创建新的属性和方法。比如:多个类中存在相同属性和方法时,将这些内容抽取到单独一个父类中,那么多个类无需再定义这些属性和方法,只需要继承父类。
示例:
//父类
class Animal {
public String name;
private double weight;
public void sleep() {
System.out.println("Animal.sleep");
}
}
//子类
class Rabbit extends Animal {
public String color;
}
//测试继承
class Test {
public static void main(String[] args) {
Rabbit rabbit = new Rabbit();
rabbit.name = "兔子"; //继承父类的属性并赋值
rabbit.color = "白色";
rabbit.sleep(); //调用继承父类的方法
System.out.println(rabbit.name); //兔子
}
}
继承好处:
- 继承的出现减少了代码冗余,提高代码的复用性。
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生关系,提供了多态的前提。
注意:不要仅仅为了获取其他类中某个功能而去继承。
多态
对象的多态性:父类引用指向子类对象,可在类继承和接口实现上体现。
声明格式:
父类类名 变量名 = new 子类构造函数;
Java引用变量类型:编译时类型和运行时类型:
- 编译时类型:声明该变量的类型。
- 运行时类型:赋值该变量的类型。
简单说:编译时看等号左边,运行时看等号右边。 当编译时类型和运行时类型不一致,就出现了对象的多态性。
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
示例:
//父类
class Animal {
public String name;
private double weight;
public void sleep() {
System.out.println("Animal.sleep");
}
}
//子类
class Rabbit extends Animal {
public String color;
public String name;
public void sleep() {
System.out.println("Rabbit.sleep");
}
}
//测试多态
class Test {
public static void main(String[] args) {
//多态,父类类型指向子类对象
Animal animal = new Rabbit();
animal.name = "兔子";
animal.sleep(); //Rabbit.sleep
System.out.println(animal.name()); //兔子
animal.color = "黑白相间"; //编译报错
}
}
在多态中,变量不能引用子类的成员变量和方法。如上代码所示,Animal父类
没有color属性,Rabbit子类
有color属性,animal变量
给color属性赋值时提示编译错误,因为编译是声明类型,声明的是Animal类型,就只能引用Animal的成员变量和方法。
调用animal.sleep()
方法返回的都是Rabbit子类的方法结果,因为运行是赋值类型,animal赋值的是Rabbit子类对象,由于Rabbit子类重写了Animal父类的sleep()
和getName()
方法,所以调用的是Rabbit子类的方法(属性没有重写)。
简单说:多态在编译时(编写代码的时候),只能引用父类的成员变量和方法,在运行时(运行代码的时候),若子类重写了父类的方法,调用的是子类重写后的方法,由于属性没有重写一说,所以引用的是继承父类同名的属性。
若我们要引用子类的成员,可以强制向下转型:
class Test {
public static void main(String[] args) {
//多态,父类类型指向子类对象
Animal animal = new Rabbit();
//向下转型:通过强制类型转换
Rabbit rabbit = (Rabbit)animal;
animal.color = "黑白相间";
System.out.println(rabbit.color); //黑白相间
}
}
注意:无继承关系的引用类型间的转换是非法的。