学习Java,就离不开学习面向对象的编程思想。Java语言是纯粹的面向对象的程序设计语言,这主要表现为Java完全支持面向对象的三种基本特征:
封装(encapsulation)
继承(inheritance)
多态(polymorphism)
Java语言完全以对象为中心,Java程序的最小程序单位是类,整个Java程序由一个一个的类组成。
万物皆对象
“面向对象”(英语:Object Oriented,简称OO)是一种以事物为中心的编程思想。
面向对象程序设计(英语:Object-oriented programming,缩写:OOP),是一种程序开发的方法。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。
面向对象时相对于面向过程而已的(c则是一个典型的面向过程的语言),站在面向对象的角度去看问题,你则是对象的动作的指挥者。如果站在面向过程的角度去看问题,你则是动作的执行者。
1.封装
为什么要封装?封装带来的好处是什么?下面以一个Person类来简述。
需求:1.描述一个Person类。定义姓名,性别的成员变量和PersonInfo的方法。
2.创建一个Person对象,为其成员变量 赋值,调用其PersonInfo方法。
①无封装
1 class Person 2 { 3 String name;//姓名 4 String gender;//性别 5 public void PersonInfo() 6 { 7 System.out.println("姓名:"+this.name+"性别:"+this.gender); 8 } 9 } 10 public class PersonDemo 11 { 12 13 public static void main(String[] args) 14 { 15 //创建一个Person对象 16 Person jack=new Person(); 17 jack.name="jack"; 18 jack.gender="男"; 19 jack.PersonInfo();//姓名:jack性别:男 20 21 //传入非法参数 22 jack.gender="不是男人"; 23 jack.PersonInfo();//姓名:jack性别:不是男人 24 25 } 26 }
总结:如果不使用封装,很容易赋值错误,并且任何人都可以更改,造成数据的不安全
②封装
封装的实现:1:对外提供公开的用于设置对象属性的public方法
----设置set
----获取get
2:在set方法中加入逻辑判断,过滤掉非法数据。
3:将所有的成员变量封装加上private,提供get、set方法
1 class Person 2 { 3 private String name;//姓名 4 private String gender;//性别 5 6 // 提供公有的get set方法 7 public String getName() 8 { 9 return name; 10 } 11 12 public void setName(String n) 13 { 14 name = n; 15 } 16 17 public String getGender() 18 { 19 return gender; 20 } 21 22 public void setGender(String gen) 23 { 24 if ("男".equals(gen) || "女".equals(gen)) 25 { 26 gender = gen; 27 28 } 29 else 30 { 31 System.out.println("请输入\"男\"或者\"女\""); 32 33 } 34 } 35 36 public void PersonInfo() 37 { 38 System.out.println("姓名:"+this.name+"性别:"+this.gender); 39 } 40 } 41 public class PersonDemo 42 { 43 44 public static void main(String[] args) 45 { 46 //创建一个Person对象 47 Person jack=new Person(); 48 jack.setName("jack"); 49 jack.setGender("男"); 50 jack.PersonInfo();//姓名:jack性别:男 51 52 //传入非法参数 53 jack.setGender("不是男人"); 54 jack.PersonInfo();//无法传入 55 56 } 57 }
可以看到实现封装之后,我们可以通过一定的手段来防止非法数据的污染,提高对象数据的安全性,并且可以隐藏类的具体实现。
2.继承
简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法(下面会讲到,这种方式叫重写)的方式,产生一个新的类型。继承是面向对象的三个基本特征--封装、继承、多态的其中之一,我们在使用JAVA时编写的每一个类都是在继承,因为在JAVA语言中,java.lang.Object类是所有类最根本的基类(或者叫父类、超类),如果我们新定义的一个类没有明确地指定继承自哪个基类,那么JAVA就会默认为它是继承自Object类的。
我们可以把JAVA中的类分为以下三种:
类:使用class定义且不含有抽象方法的类。
抽象类:使用abstract
class定义的类,它可以含有,也可以不含有抽象方法。
接口:使用interface定义的类。
在这三种类型之间存在下面的继承规律:
①类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
②抽象类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。`
③接口只能继承(extends)接口。
类和抽象类都只能最多继承一个类,或者最多继承一个抽象类,并且这两种情况是互斥的,也就是说它们要么继承一个类,要么继承一个抽象类。
类、抽象类和接口在继承接口时,不受数量的约束,理论上可以继承无限多个接口。当然,对于类来说,它必须实现它所继承的所有接口中定义的全部方法。
抽象类继承抽象类,或者实现接口时,可以部分、全部或者完全不实现父类抽象类的抽象(abstract)方法,或者父类接口中定义的接口。
类继承抽象类,或者实现接口时,必须全部实现父类抽象类的全部抽象(abstract)方法,或者父类接口中定义的全部接口。
继承给我们的编程带来的好处就是对原有类的复用(重用)。
1.子类继承父类的成员变量
当子类继承了某个类之后,便可以使用父类中的成员变量,但是并不是完全继承父类的所有成员变量。具体的原则如下:
1)能够继承父类的public和protected成员变量;不能够继承父类的private成员变量;
2)对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承;
3)对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生隐藏现象,即子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中同名成员变量,需要使用super关键字来进行引用。
2.子类继承父类的方法
1)能够继承父类的public和protected成员方法;不能够继承父类的private成员方法;
2)对于父类的包访问权限成员方法,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承;
3)对于子类可以继承的父类成员方法,如果在子类中出现了同名称的成员方法,则称为覆盖,即子类的成员方法会覆盖掉父类的同名成员方法。如果要在子类中访问父类中同名成员方法,需要使用super关键字来进行引用。
3.构造器
子类是不能够继承父类的构造器,但是要注意的是,如果父类的构造器都是带有参数的,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类有无参构造器,则在子类的构造器中用super关键字调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。
super关键字
需求:定义一个Son(子类)继承Father(父类)
1 class Father { 2 int x = 1; 3 4 Father() { 5 System.out.println("这是父类无参构造"); 6 } 7 8 Father(int x) { 9 10 this.x = x; 11 System.out.println("这是父类有参构造"); 12 } 13 14 void speak() { 15 System.out.println("我是父亲"); 16 } 17 } 18 19 class Son extends Father { 20 int y = 1; 21 22 Son() { 23 System.out.println("这是子类的无参构造"); 24 } 25 26 Son(int y) { 27 28 this.y = y + x; 29 System.out.println("这是子类的有参构造"); 30 } 31 32 void run() { 33 super.speak(); // 访问父类的函数 34 System.out.println("我是儿子"); 35 } 36 } 37 38 class Demo{ 39 40 public static void main(String[] args) { 41 Son s = new Son(3); 42 System.out.println(s.y);// 4 43 } 44 }
super关键字作用
1:主要存在于子类方法中,用于指向子类对象中父类对象。
2:访问父类的属性
3:访问父类的函数
4:访问父类的构造函数
super注意
1:this和super很像,this指向的是当前对象的调用,super指向的是当前调用对象的父类。
2:子类的构造函数默认第一行会默认调用父类无参的构造函数,隐式语句
super();
1:父类无参构造函数不存在,编译报错。
Son(int y) { //super();隐式语句 this.y = y + x; System.out.println("这是子类的有参构造"); }
3:子类显式调用父类构造函数
在子类构造函数第一行通过super关键字调用父类任何构造函数。如果显式调用父类构造函数,编译器自动添加的调用父类无参数的构造就消失。构造函数间的调用只能放在第一行,只能调用一次。super() 和this()不能同时存在构造函数第一行。
1 Son(int y) { 2 super(y);// 子类显式调用父类构造函数 3 this.y = y + x; 4 System.out.println("这是子类的有参构造"); 5 }
重写(Override)
1:前提
1)必须要有继承关系
2:特点
1)当子类重写了父类的函数,那么子类的对象如果调用该函数,一定调用的是重写过后的函数。
可以通过super关键字进行父类的重写函数的调用。
2) 继承可以使得子类增强父类的方法
3:细节
1) 函数名必须相同
2)参数列表必须相同
3)子类重写父类的函数的时候,函数的访问权限必须大于等于父类的函数的访问权限否则编译报错
4)子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类
3.多态
从字面上理解,多态就是一种类型表现出多种状态。从一定角度来说,封装和继承是多态得以实现的基础。
多态前提:类与类之间有关系,继承父类或者实现接口。
多态体现
1:父类引用变量指向了子类的对象
2:父类引用也可以接受自己的子类对象
多态给我们带来的好处是消除了类之间的耦合关系,使程序更容易扩展。
一个简单的需求:用多态模拟 移动硬盘或者U盘或者MP3插到电脑上进行读写数据
1 //抽象的父类 2 abstract class MobileStorage 3 { 4 public abstract void Read(); 5 public abstract void Write(); 6 } 7 8 class MobileDisk extends MobileStorage 9 { 10 public void Read() 11 { 12 System.out.printf("移动硬盘在读取数据"); 13 } 14 public void Write() 15 { 16 System.out.printf("移动硬盘在写入数据"); 17 } 18 } 19 class UDisk extends MobileStorage 20 { 21 public void Read() 22 { 23 System.out.printf("U盘在读取数据"); 24 } 25 26 public void Write() 27 { 28 System.out.printf("U盘在写入数据"); 29 } 30 } 31 class Mp3 extends MobileStorage 32 { 33 public void Read() 34 { 35 System.out.printf("MP3在读取数据"); 36 } 37 38 public void Write() 39 { 40 System.out.printf("Mp3在写入数据"); 41 } 42 43 public void PlayMusic() 44 { 45 System.out.printf("MP3自己可以播放音乐"); 46 } 47 } 48 49 class Computer 50 { 51 MobileStorage Ms; 52 53 public void CpuRead() 54 { 55 Ms.Read(); 56 } 57 58 public void CpuWrite() 59 { 60 Ms.Write(); 61 } 62 } 63 64 public class Demo { 65 66 public static void main(String[] args) 67 { 68 //类型提升,向上转型。new Mp3();//new MobileDisk();//new UDisk(); 69 //但是不能使用子类的特有方法。 70 MobileStorage ms = new UDisk(); 71 Computer cpu = new Computer(); 72 cpu.Ms = ms; 73 cpu.CpuRead(); 74 cpu.CpuWrite(); 75 76 //向下转型,可以强制将父类的引用转换成子类类型。 使用子类特有方法 77 MobileStorage ms2=new Mp3(); 78 if(ms2 instanceof Mp3) 79 { 80 Mp3 mp3=(Mp3)ms2; 81 mp3.PlayMusic(); 82 } 83 } 84 85 }