Java——面向对象编程
面向对象简介
面向对象编程是一种编程方式,此编程方式需要使用 “类” 和 “对象” 来实现。
- 类,用于描述多个对象的共同特征,它是对象的模板。
- 对象,用于描述现实中的个体,它是类的实例。
类的定义
class 类名 { //属性 数据类型 变量名; … //方法 修饰符 返回值类型 方法名(参数){ } … }
创建对象
类名 对象名 = new 类名();
面向对象编程的优点
最大的好处是:代码重用
1、易维护
采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。
2、质量高
在设计时,可重用现有的,在以前的项目的领域中已被测试过的类使系统满足业务需求并具有较高的质量。
3、效率高
在软件开发时,根据设计的需要对现实世界的事物进行抽象,产生类。使用这样的方法解决问题,接近于日常生活和自然的思考方式,势必提高软件开发的效率和质量。
4、易扩展
由于继承、封装、多态的特性,自然设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低。
面向对象三大特性
一、封装
将对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式。
封装的体现:
- 变量:使用 private 修饰,这就是变量的封装
- 方法(无论是用什么字段修饰):也是一种封装,封装了多条代码
- 类(无论是用什么字段修饰):也是一种封装,封装了多个方法
私有成员变量:
- private修饰的成员只能在当前类中访问,其他类中无法直接访问
class Person { private int age; // 设置类私有成员变量(只能将属性变量设置为私有,局部变量不可以) private String name; public void setAge(int a) { // 对外提供设置成员变量的方法 if (a < 0 || a > 130) { // 由于是设置成员变量的值,这里可以加入数据的验证 System.out.println(a + "不符合年龄的数据范围"); return; } age = a; } public void getAge() { // 对外提供访问成员变量的方法 return age; } }
this关键字:
作用:用来区别同名的成员变量与局部变量(this.成员变量)
示例:判断是否是同龄人
class Person { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void speak() { System.out.println("name=" + this.name + ",age=" + this.age); } // 判断是否为同龄人 public boolean equalsAge(Person p) { // 使用当前调用该equalsAge方法对象的age和传递进来p的age进行比较 // 由于无法确定具体是哪一个对象调用equalsAge方法,这里就可以使用this来代替 /* * if(this.age == p.age) { return true; } return false; */ return this.age == p.age; } }
二、继承
面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
注:继承不能继承父类的私有成员(变量+方法)
使用格式:
class 子类 extends 父类{}
Employee.java(父类)
package cn.xinge.test; //定义员工类Employee public class Emolpyee { String name; public void work(){ System.out.println("努力工作中!"); } }
Developer.java(子类)
package cn.xinge.test; //定义研发部员工类Developer 继承 员工类Employee public class Developer extends Emolpyee { public void printName(){ System.out.println("name="+name); } }
Test.java(测试类)
package cn.xinge.test; public class Test { public static void main(String[] args){ Developer d = new Developer(); // 创建一个研发部员工类对象 d.name = "想54256"; // 为该员工类的name属性进行赋值 d.printName(); // 调用该员工的printName()方法 d.work(); // 调用Developer类继承来的work()方法 } }
继承的优点:
- 继承的出现提高了代码的复用性,提高软件开发效率。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
使用的注意事项:
a.在Java中,类只支持单继承,不允许多继承,也就是说一个类只能有一个直接父类,例如下面这种情况是
package cn.xinge.test; public class Test { public static void main(String[] args){ Developer d = new Developer(); // 创建一个研发部员工类对象 d.name = "想54256"; // 为该员工类的name属性进行赋值 d.printName(); // 调用该员工的printName()方法 d.work(); // 调用Developer类继承来的work()方法 } }
b.多个类可以继承一个父类,例如下面这种情况是允许的。
class A{} class B extends A{} class C extends A{} // 类B和类C都可以继承类A
c.在Java中,多层继承是可以的,即一个类的父类可以再去继承另外的父类,例如C类继承自B类,而B类又可以去继承A类,这时,C类也可称作A类的子类。下面这种情况是允许的。
class A{} class B extends A{} // 类B继承类A,类B是类A的子类 class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类
d.在Java中,子类和父类是一种相对概念,也就是说一个类是某个类父类的同时,也可以是另一个类的子类。例如上面的这种情况中,B类是A类的子类,同时又是C类的父类。
重写父类的方法和变量
Phone.java
//手机类 class Phone{ public void sendMessage(){ System.out.println("发短信"); } public void call(){ System.out.println("打电话"); } public void showNum(){ System.out.println("来电显示号码"); } }
NewPhone.java
//智能手机类 class NewPhone extends Phone{ //覆盖父类的来电显示号码功能,并增加自己的显示姓名和图片功能 public void showNum(){ //子类中调用父类已经存在的功能使用super super.showNum(); //增加自己特有显示姓名和图片功能 System.out.println("显示来电姓名"); System.out.println("显示头像"); } }
三、多态
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。
最终多态体现为父类引用变量可以指向子类对象。
多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
普通类的多态:
//使用格式:父类 变量名 = new 子类(); class Fu {} class Zi extends Fu {} Fu f = new Zi(); // 多态
抽象类的多态:
//使用格式:抽象类 变量名 = new 抽象类子类(); //父类 public abstract class Fu { public abstract void method(); } //子类 public class Zi extends Fu { public void method(){ System.out.println("重写父类抽象方法"); } } Fu fu= new Zi();
接口的多态:
// 使用格式:接口 变量名 = new 接口实现类(); //父接口 public interface Fu { public abstract void method(); } // 实现父接口 class Zi implements Fu { public void method(){ System.out.println("重写接口抽象方法"); } } //接口的多态使用 Fu fu = new Zi();
多态的特点
多态成员变量:
Fu.java
package cn.xinge.test3; public class Fu { int num = 4; }
Zi.java
package cn.xinge.test3; public class Zi extends Fu{ int num = 5; }
Test.java
package cn.xinge.test3; public class Text { public static void main(String[] args){ Fu test = new Zi(); System.out.println(test.num); Zi test2 = new Zi(); System.out.println(test2.num); } }
输出结果
4,5
结论:
当子父类中出现同名的成员变量时,多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
运行时期:也是调用引用型变量所属的类中的成员变量。
简单记:编译和运行都参考等号的左边。编译运行看左边。
多态成员方法:
Fu.java
package cn.xinge.test3; public class Fu { int num = 4; public void show(){ System.out.println("父类"); } }
Zi.java
package cn.xinge.test3; public class Zi extends Fu{ int num = 5; public void show(){ System.out.println("子类"); } }
Test.java
package cn.xinge.test3; public class Text { public static void main(String[] args){ Fu test = new Zi(); System.out.println(test.num); test.show(); Zi test2 = new Zi(); System.out.println(test2.num); test2.show(); } }
测试结果
4,子类,5,子类
instanceof关键字
我们可以通过instanceof关键字来判断某个对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类。
使用格式:
boolean b = 对象instanceof 数据类型;
Person p1 = new Student(); // 前提条件,学生类已经继承了人类 boolean flag = p1 instanceof Student; //flag结果为true boolean flag2 = p2 instanceof Teacher; //flag结果为false
转型
多态的转型分为向上转型与向下转型两种:
-
向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:
父类类型变量名= new 子类类型();
如:Person p = new Student();
-
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
使用格式:
子类类型变量名= (子类类型) 父类类型的变量;
如:Student stu = (Student) p; //变量p 实际上指向Student对象
多态的好处与弊端
当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。向上转型的好处是隐藏了子类类型,提高了代码的扩展性。
但向上转型也有弊端,只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。看如下代码
//描述动物类,并抽取共性eat方法 abstract class Animal { abstract void eat(); } // 描述狗类,继承动物类,重写eat方法,增加lookHome方法 class Dog extends Animal { void eat() { System.out.println("啃骨头"); } void lookHome() { System.out.println("看家"); } } // 描述猫类,继承动物类,重写eat方法,增加catchMouse方法 class Cat extends Animal { void eat() { System.out.println("吃鱼"); } void catchMouse() { System.out.println("抓老鼠"); } } public class Test { public static void main(String[] args) { Animal a = new Dog(); //多态形式,创建一个狗对象 a.eat(); // 调用对象中的方法,会执行狗类中的eat方法 // a.lookHome();//使用Dog类特有的方法,需要向下转型,不能直接使用 // 为了使用狗类的lookHome方法,需要向下转型 // 向下转型过程中,可能会发生类型转换的错误,即ClassCastException异常 // 那么,在转之前需要做健壮性判断 if( !a instanceof Dog){ // 判断当前对象是否是Dog类型 System.out.println("类型不匹配,不能转换"); return; } Dog d = (Dog) a; //向下转型 d.lookHome();//调用狗类的lookHome方法 } }
什么时候使用向上转型:
当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
如:Animal a = new Dog();
a.eat();
什么时候使用向下转型:
当要使用子类特有功能(父类没有的方法)时,就需要使用向下转型。
如:Dog d = (Dog) a; //向下转型
调用狗类的lookHome方法
向下转型的好处和弊端:
好处是:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
弊端是:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。
如:if( !a instanceof Dog){…}
总结
-
封装:把对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式
-
继承:子类会自动拥有父类所有可继承的属性和方法。
-
多态:配合继承与方法重写提高了代码的复用性与扩展性;如果没有方法重写,则多态同样没有意义。
- 多态就是同一个接口,使用不同的实现类执行不同的操作
- 三大条件:
- 继承或实现
- 方法重写
- 父类引用指向子类对象
注:就是父类写一个方法,然后子类重写那个方法,在下次调用的时候,就会执行的是子类重写的方法