一、java中的访问权限
java中的访问权限共4种,private、default(默认修饰符)、protected、public,他们的作用域如下:
(二)封装
封装的体现:
属性的封装
方法的封装
构造函数的封装(单例模式)
将类的某些信息隐藏在类的内部,不让外部程序直接访问,而是提供操作数据的方法,这就是属性的封装性
封装的实现:
属性的封装:使用private修饰符修饰成员变量,然后根据需求定义getter或setter方法。
方法的封装:有些方法仅仅只是在类的内部使用,不希望外部程序调用它,通过private修饰即可。
构造函数的封装:使用private修饰构造器,不让外部程序创建类对象,在类内部实例化对象,并提供公共接口。形如单例模式。
开发建议:在定义类的时候,所有的属性都要用private封装,封装的属性如果需要被外部操作,提供对应的getter和setter方法。
举例代码
1. class Person{//属性封装,封装了age和name两个属性,提供了对应的getter和setter方法 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; } } 2. private void swap(int[]arr,int m, int n){ //方法的封装,只有内部方法才能调用 int temp=arr[m]; arr[m]=arr[n]; arr[n]=temp; } 3. public class JavaTest { public static void main(String[] args) { Person p1=Person.getPersonInstance(); } } class Person{ private Person(){} //构造器的封装 public static Person getPersonInstance(){ return new Person(); } }
简单示例:
所谓封装就是访问权限控制,这里的设置不允许调用者直接访问类的属性,使用private修饰,将属性藏起来
此时若创建对象,直接修改属性值,则会报错
想要对属性进行读写则需要在类中添加get/set方法
此时就可以进行属性的读写了
封装的好处
只能通过规定方法访问数据
隐藏类数据的实现细节
方便修改实现
方便加入控制语句
可以修改属性的读写权限
(三)继承
思考一下:为什么会有继承?
当多个类中存在相同属性和行为时,为了提高代码复用性,将这些相同的内容抽取到一个单独的类中,这些多个类无需再定义这些属性和行为,只要去继承那一个单独的类即可。此处的多个类称为子类(派生类),单独的这个类称为父类(基类或超类)。
继承就是子类继承父类‘所有’的特征和行为,使得子类对象(实例)具有父类的成员变量和方法,还可以在此基础上添加新方法和成员变量来满足需求。
当创建一个类时,总是在继承。Java中的类没有显式的继承任何类,则默认继承Object类,Object类是Java语言提供的根类,也就是说,一个对象与生俱来就有Object类当中的所有特征。
注意
子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访到的。即只是拥有,但是无法使用。(这里不考虑Java反射机制)但是如果子类中公有的方法影响到了父类私有属性,那么私有属性是能够被子类使用到的。
不能被继承的成员父类:构造方法,构造函数。但是子类可以通过super()显示父类的构造函数。
如上图所示,动物继承生物类,老虎又继承动物类。从这个例子可以看出:越往上的类越抽象,越往下的类越具体。
继承规则
Java只支持单继承和多层继承,不允许多重继承
一个类可以被多个子类继承,一个类只能继承一个父类(直接父类),使用extends关键字
子父类是相对的概念
子类直接继承的父类,称为直接父类;间接继承的父类称为间接父类。
子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
代码实例
class Biology{ //生物类 String name; int age; } class Animal extends Biology{//动物类继承了生物类,生物类是动物类的直接父类 } class Plant extends Biology{//植物类继承了生物类,生物类是植物类的直接父类 } class Tiger extends Animal{//老虎类继承了动物类,动物类是老虎类的直接父类,生物类是老虎类的间接父类 } class Tree extends Plant{//树类继承了植物类,植物类是树类的直接父类,生物类是植物类的间接父类 }
方法重写
在子类中,可以根据需要对从父类中继承来的方法进行方法重写。重写以后,当子类对象调用与父类的同名同参数的方法时,优先调用子类重写的方法。
当子类调用属性和方法的时候,如果自己类里面有该属性和方法,肯定先调用自己的,如果自己类里面没有,就去父类(先直接父类/再间接父类)找,一直找到Object类为止。
方法重写有如下要求:
子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表;
子类重写的方法的返回值类型不能大于父类被重写方法的返回值类型
即:
1.父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void。
2.父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以使A类或A类的子类。
3. 父类被重写的方法的返回值类型是基本数据类型(比如double),则子类重写的方法的返回值类型必须是相同的基本数据类型。
子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
子类方法抛出的异常不能大于父类被重写方法的异常
子类能重写的都是非static的方法
思考:如果父类有个方法是private访问权限,在子类中将此方法声明为default访问权限,这个叫方法重写吗?达咩,当然不是。
super关键字
在Java类中,使用super调用父类中的指定操作。可以访问父类中的构造器、属性和成员方法。
我们需要掌握的内容:
super的三种语法格式:访问父类构造器(必须放构造函数中的第一行):super()或super(参数);访问父类属性:super.属性;访问父类方法:super.方法名()。
我们可以在子类的方法或构造器中,通过使用super.或super(),显式地调用父类中声明的属性/方法或构造器。通常情况下,我们习惯省略‘super.’。
特殊情况,若想在子类中调用父类的同名属性或被重写方法时,必须显式地使用'super.'。
子类中所有的构造器默认都会访问父类中空参数的构造器。
当父类中没有空参数的构造器时,子类的构造器必须通过super(参数列表)语句指定父类中相应的构造器,必须放在构造器的首行。目的在于在创建子类对象的时候,先初始化父类。
如果类里面有n个构造器,那么可以调用n-1次,至少1次是调父类的。
this()与super()不能同时出现,在构造器中,必须二选一,且只能出现在第一行。
结论:在java语言中,不管new什么对象,最后Object类中的无参构造方法是一定会执行的。
代码举例:
public class JavaTest { public static void main(String[] args) { Chinese cn=new Chinese(); cn=new Chinese("I 'm Chinese!"); cn=new Chinese("I 'm Chinese!",18); cn.sout(); } } class Person{ int id=431234566; String name; public Person(){} Person (String name){ this.name=name; } public void sout(){ System.out.println("这是父类中的输出方法1"); } } class Chinese extends Person{ public Chinese(){ super(); //调用父类无参构造方法,这个子类每个构造器都会默认调用的,可不写 } public Chinese(String name){ super(name);//调用父类有参构造方法 } public Chinese(String name,int age){ this(name);//通过this去调用构造器中的super方法 System.out.println("父类中的id"+super.id);//调用父类的属性 } @Override public void sout() { System.out.println("这是子类中的输出方法!"); super.sout();//调用父类的sout方法 } }
输出结果是
父类中的id431234566
这是子类中的输出方法!
这是父类中的输出方法1
super与this的区别
super()函数在子类构造函数中调用父类的构造函数时使用,而且必须要在构造函数的第一行。
this()函数调用本类中另一种形成的构造函数。this()只能用在构造函数中,并且也只能在第一行。
super()和this()都指的是对象,不能同时出现在构造函数里,只能二选一,均不可在static环境中使用。
super:引用当前对象的父类中的成员。语法格式:super.属性名/方法名()
this:代表当前对象名。this是对象的隐藏属性 (本质就是一个普通的成员变量),和其他non-static 属性一样,在创建对象的时候会为每个新对象分配该对象的专属成员变量(this就是其中一个),this这个成员变量存储在堆内存中,该变量的值是所属对象在堆内存的地址。
this代表本类对象的引用,super代表父类的内存空间的标识。
从本质上来讲,this是一个面向对象的指针,然后super是一个Java关键字。
接下来用一个简单的例子来说明一下
父类Pet中有属性 name 和 age 和方法 eat 和sleep
子类Cat使用extend关键字继承了父类Pet,中间什么都没写
继承的好处:
提高代码复用性,减少代码冗余
便于功能扩展
为多态性的使用,提供了前提
继承也存在一些缺点,继承的类与类之间的耦合度非常高,不利于代码的扩展。
(四) 多态
多态是指同一个行为具有多个表现形式,多态的使用有三个条件
多态的前提:
1. >类之间要有继承的关系
2. 子类要有对父类方法的重写(不重写也不会报错,但这样体现不出来多态的意义)
3. 父类的引用指向子类对象
注:可以直接应用在抽象类和接口上。
多态的总结
一个对象的编译类型和运行类型可以不一致。编译类型是在定义对象时,就确定了,不能改变;运行类型是可以变化的。
编译类型看定义时=左边,运行类型看=右边。
多态特点:可以调用父类中所有成员(需遵守访问权限),不能调用子类中特有成员;最终运行效果要看子类的具体实现。
当使用多态方式调用方法时,首先检查父类中是否有所调用的方法,如果没有,则编译错误;如果有,再去调用子类(运行类型)中的重写方法。
想调用子类中所有的成员,可以进行向下转型,语法:子类类型 引用名=(子类类型) 父类引用。一般推荐使用instanceof 去判断一下。
对象 instanceof 类名,用于判断对象的运行类型是否为××类型或××类型的子类型。
java动态绑定机制:当调用对象方法时,该方法会和对象的内存地址/运行内存绑定;调用属性时,没有绑定机制,哪里声明哪里使用。
对象类型转换:向上转型:父类引用指向子类对象,对象多态性;向下转型:子类引用指向父类对象,必须强转。
代码举例:
public class JavaTest { public static void main(String[] args) { A a=new B(); System.out.println(a.sum()); System.out.println(a.sum1()); } } class A{//父类 int i=10; public int sum(){ return getI()+10; } public int sum1(){ return i+10; } public int getI(){ return i; } } class B extends A{//子类 int i=20; public int getI(){ return i; } }
猜猜答案是多少,猜对了就说明你真正意义上知道动态绑定机制了。答案是30 20
接下来用一个例子来说明一下
子类Cat继承父类Pet,并且重写了Pet类中的eat与sleep方法,
此时注意到创建对象时 Pet pet =new Cat();,Pet类型的引用指向了Cat的对象,
并且由于重写了Pet中的方法,调用的eat与sleep方法是子类Cat中的返回值
多态作用
提高了代码的通用性,常称作接口重用。
最后再啰嗦一句:面向对象编程的本质是:以类的方式组织代码,以对象的组织(封装)数据。
本文作者为BUG之神,转载请注明。