一、封装
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
将属性隐藏起来,提供公共方法访问某个属性。
1、private关键字
- private是一个权限修饰符,代表最小权限。
- 可以修饰成员变量和成员方法。
- 被private修饰后的成员变量和成员方法,只在本类中才能访问。
2、this关键字
this代表所在类的当前对象的引用(地址值),即对象自己的引用。
方法被哪个对象调用,方法中的this就代表那个对象。谁在调用,this就代表谁。
this使用格式
this.成员变量名;
使用 this
修饰方法中的变量,解决成员变量被隐藏的问题,代码如下:
public class Student { private String name; public void setName(String name) { //name = name; this.name = name; } public String getName() { return name; } }
3、构造方法
当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效。
构造方法的定义格式
修饰符 构造方法名(参数列表){ // 方法体 }
构造方法的写法上,方法名与它所在的类名相同。它没有返回值,所以不需要返回值类型,甚至不需要void。使用构造方法后,代码如下:
public class Student { private String name; private int age; // 无参数构造方法 public Student() {} // 有参数构造方法 public Student(String name,int age) { this.name = name; this.age = age; } }
注意事项:
- 如果你不提供构造方法,系统会给出无参数构造方法。
- 如果你提供了构造方法,系统将不再提供无参数构造方法。
- 构造方法是可以重载的,既可以定义参数,也可以不定义参数。
4、标准代码
JavaBean
是 Java语言编写类的一种标准规范。符合JavaBean
的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的set
和get
方法。
public class ClassName{ //成员变量 //构造方法 //无参构造方法【必须】 //有参构造方法【建议】 //成员方法 //getXxx() //setXxx() }
编写符合JavaBean
规范的类,以学生类为例,标准代码如下:
public class Student { //成员变量 private String name; private int age; //构造方法 public Student() {} public Student(String name,int age) { this.name = name; this.age = age; } //成员方法 publicvoid setName(String name) { this.name = name; } public String getName() { return name; } publicvoid setAge(int age) { this.age = age; } publicint getAge() { return age; } }
测试类,代码如下:
public class TestStudent { public static void main(String[] args) { //无参构造使用 Student s= new Student(); s.setName("柳岩"); s.setAge(18); System.out.println(s.getName()+"---"+s.getAge()); //带参构造使用 Student s2= new Student("赵丽颖",18); System.out.println(s2.getName()+"---"+s2.getAge()); } }
二、继承
1、概述
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。如图所示:
其中,多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。
继承描述的是事物之间的所属关系。例如,图中兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
2、定义
继承:就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
优点:
- 提高代码的复用性。
- 类与类之间产生了关系,是多态的前提。
3、格式
通过 extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 { ... } class 子类 extends 父类 { ... }
4、继承后的特点
成员变量:
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super
关键字,修饰父类成员变量。
构造方法:
当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先我们要回忆两个事情,构造方法的定义格式和作用。
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个
super()
,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
成员方法:
成员方法不重名
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。
成员方法重名——重写(Override)
如果子类父类中出现重名的成员方法,需要进行方法重写(Override)。
- 方法重写 :子类中出现与父类一模一样的方法时,需要重写父类的方法。
子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。
class Phone { public void showNum(){ System.out.println("来电显示号码"); } }
extends:
//智能手机类 class NewPhone extends Phone { //重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能 public void showNum(){ //调用父类已经存在的功能使用super super.showNum(); //增加自己特有显示姓名和图片功能 System.out.println("显示来电姓名"); System.out.println("显示头像"); } }
Test:
public class ExtendsDemo06 { public static void main(String[] args) { // 创建子类对象 NewPhone np = new NewPhone(); // 调用子类重写的方法 np.showNum(); } }
注意事项:
- 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
- 子类方法覆盖父类方法,子类的返回值类型要是父类返回值类型的子类。
5、super和this区别
父类空间优先于子类对象产生
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。理解图解如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gggegzp7-1592739295232)(img/2.jpg)]
super和this的含义
- super :代表父类的存储空间标识(可以理解为父亲的引用)。
- this :代表当前对象的引用(谁调用就代表谁)。
特点:
- 子类的每个构造方法中均有默认的super(),调用父类的空参构造。
- 手动调用父类构造会覆盖默认的super()。
- super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
6、继承的特点
Java只支持单继承。
三、多态
1、概述
- 多态: 是指同一行为,具有多个不同表现形式。
前提:
- 继承或者实现【二选一】
- 方法的重写【意义体现:不重写,无意义】
- 父类引用指向子类对象【格式体现】
2、格式
多态体现的格式:
父类类型 变量名 = new 子类对象; 变量名.方法名();
代码:
Fu f = new Zi(); f.method();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;
如果有,执行的是子类重写后方法。
3、多态的应用
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
定义父类:
public abstract class Animal { public abstract void eat(); }
定义子类:
class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 多态形式,创建对象 Cat c = new Cat(); Dog d = new Dog(); showAnimalEat(c); showAnimalEat(d); } public static void showAnimalEat (Animal a){ a.eat(); } }
由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。
多态的好处,可以使程序编写的更简单,并有良好的扩展。
4、向上转型和向下转型
- 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
当父类引用指向一个子类对象时。
父类类型 变量名 = new 子类类型(); 如:Animal a = new Cat();
- 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式。
子类类型 变量名 = (子类类型) 父类变量名; 如:Cat c =(Cat) a;
5、ClassCastException异常
代码能通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验。
变量名 instanceof 数据类型 如果变量属于该数据类型,返回true。 如果变量不属于该数据类型,返回false。
所以,转换前,我们最好先做一个判断,代码如下:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 if (a instanceof Cat){ Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } else if (a instanceof Dog){ Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse } } }
6、interface作为参数传递
当接口作为方法的参数时,需要传递它的子类对象。
- 接口作为参数时,传递它的子类对象。
- 接口作为返回值类型时,返回它的子类对象。
定义方法:
public static void printEvenNum(List list) { // 遍历集合list for (int i = 0; i < list.size(); i++) { Object num = list.get(i); System.out.println(num); } }
调用方法:
public class Test { public static void main(String[] args) { // 创建ArrayList集合,并添加数字 ArrayList<Integer> srcList = new ArrayList<>(); srcList.add(10); //调用方法,传递参数 printEvenNum(srcList); } }