第五章 面向对象
作者:互联网
第五章 面向对象
1 面向对象概念
-
面向对象编程(Objected-Oriented Programming)OOP
-
面向对象编程的本质就是:以类的方式组织代码,以对象的形式组织(封装)数据
-
抽象
-
三大特性:
- 封装
- 继承
- 多态
-
从认识论角度考虑是先有对象后有类,对象是具体的事物,类是抽象的,是对对象的抽象
-
从代码运行角度考虑是先有类后有对象,类是对象的模板
2 方法的调用
2.1 静态方法与非静态方法
eg.:现有类名Student
,该类含有静态方法public static void a(){}
和非静态方法public void b(){}
静态方法与非静态方法区别在于:
- 调用静态方法:可以直接
Student.a();
来调用静态方法 - 调用非静态方法:必须先new一个对象再调用非静态方法,eg.:
Student student1 = new Student(); student1.b();
- 或者
new Student().b();
2.1.1 方法与方法之间的调用
若两个方法都是非静态方法,则可以互相调用
若两个方法都是静态方法,则可以互相调用
若一个是静态方法,一个是非静态方法,则静态方法不可调用非静态方法,非静态方法可以调用静态方法
- 因为非静态方法是创建对象后才存在,所以调用非静态方法需要先new一个对象(即实例化)
2.2 值传递和引用传递
值传递术后
package com.oop.demo01;
public class Demo02 {
public static void main(String[] args) {
int a = 1;
System.out.println(a);//输出为1
Demo02.change(a);
System.out.println(a);//输出为1
}
public static void change(int a){
a = 10;//无返回值,不会对实参产生影响
}
}
值传递不会改变变量的值
引用传递
对象是通过引用来操作的:对象是在堆里的
package com.oop.demo01;
public class Demo03 {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.name);//输出为null
change(student);
System.out.println(student.name);//输出为Liam
}
public static void change(Student student){
student.name= "Liam";
}
}
//定义了一个Student类,有一个属性:name
class Student{
String name;//null
}
引用传递会改变变量的值
3 类与对象的关系
3.1 this关键字
this关键字指代当前类,编写类的时候可以用this打点来调用当前类的属性和方法
3.2 类与类之间的交互调用
- 一个项目会有一个专门放main方法的Application类,其余类不会放main方法
- 一个类有两个部分:属性(即字段),方法
eg.:
Student类:
package com.oop.demo02;
public class Student {
//属性:字段
String name;
int age;
//方法
public void study(){
System.out.println(this.name+"在学习");//this代表当前类
}
}
Application类:
package com.oop.demo02;
public class Application {
public static void main(String[] args) {
Student xiaoming = new Student();
xiaoming.name = "小明";
xiaoming.age = 23;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
Student xiaohong = new Student();
xiaohong.name = "小红";
xiaohong.age = 22;
System.out.println(xiaohong.name);
System.out.println(xiaohong.age);
}
}
4 ※ 构造器 ※
-
构造器作用:可以初始化实例对象的属性的值
-
使用new关键字本质是在调用构造器
-
创建对象时会调用构造方法,调用完成时才创建完成一个对象
-
使用new关键字创建对象时,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
-
构造器格式:
public 类名(){}
,构造器的名字必须跟类的名字相同,必须没有返回类型,也不能有void -
构造器分为:有参构造器,无参构造器
new一个对象时会跳转到类的默认的无参构造器,这是默认会有的构造器,即使什么构造器都不写在相应的class文件中也会有一个
public 类名(){}
Student.java文件如下图:
Student.class文件如下图:
4.1 构造方法(即构造器)的重载
-
构造方法(即构造器)也是有重载的,创建对象时,不传参数就调用无参构造方法,传一个参数就调用含有一个形参的构造方法,传两个参数就调用含有两个形参的构造方法,以此类推
-
创建构造器的快捷键:alt+insert再选中constructor,选择要创建构造器包含的属性
构造器重载示例
类部分:
package com.oop.demo02;
public class Person {
String name;
int age;
char gender;
public Person() {
}
public Person(String name){
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, char gender) {
this.name = name;
this.gender = gender;
}
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
main部分:
package com.oop.demo02;
public class Application {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person("liam");
Person person3 = new Person("liam",23);
Person person4 = new Person("liam",'男');
Person person5 = new Person("liam",23,'男');
}
}
【注意】即使在java文件中什么都不写,也会默认有一个不显示的无内容的无参构造器,但是一旦定义了有参构造,再想使用无参构造,无参构造就必须显示定义
package com.oop.demo02;
public class Person {
String name;
//构造器作用:可以对实例对象的初始默认值进行设定
//无参构造器:
public Person(){
}
//有参构造器:一旦定义了有参构造,再想使用无参构造,无参构造就必须显示定义
public Person(String name){
this.name = name;
}
}
5 对象创建过程中的内存分析
现有Pet类部分:
package com.oop.demo02;
public class Pet {
String name;
int age;
public void shout(){
System.out.println("叫了一声");
}
}
main方法部分:
package com.oop.demo02;
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout();
Pet cat = new Pet();
}
}
内存分析如下图所示:
6 封装
封装(数据的隐藏)
- 通常应禁止直接访问一个对象中的数据,而应该通过操作接口来访问,这也成为信息隐藏
- 程序的设计应该追求“高内聚,低耦合”
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合:仅暴露少量的方法给外部使用
封装的意义
可以给私有属性一些条件,用以约束属性的范围
6.1 private关键字
eg.现有Student类:
public class Student {
private String name;
private int id;
private char gender;
}
通过private关键字让属性私有化,这使得不能直接用对象名打点来对属性进行赋值操作,与之对应的是public关键字
6.2 私有属性的设置set与获取get
- 为了能够使用与操作私有化的属性,需要一个获得这个属性的方法getName()以及设置这个属性的方法setName()
- 快捷键:alt+insert再选中Getter and Setter,选择要创建set和get方法的属性
eg.类部分:
package com.oop.demo03;
public class Student {
private String name;
private int id;
private char gender;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 120|age < 0){
System.out.println("ERROR");
}else {
this.age = age;
}
}//给age属性设置约束,以保证不能随心所欲的设置age的值
}
main部分:
package com.oop;
import com.oop.demo03.Student;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setAge(130);
System.out.println(s1.getAge());
}
}
最后main部分输出结果:
因为age设置了超过设定范围所以输出了ERROR,age没有被成功赋值,还是保持了默认的0
7 继承
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类),子类继承父类,使用关键字extends来表示
- extends的意思是“扩展”,子类是父类的扩展
- JAVA中类只有单继承,没有多继承:就是一个儿子只能有一个父亲,但是一个父亲可以有多个儿子
- 继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等
- 子类和父类之间,从意义上讲应该具有父类包含子类的关系
子类会继承父类的全部方法和public属性,并不会继承父类的private属性以及private方法
父类部分:
package com.oop.demo05;
public class Person {
private int money = 10_0000_0000;
public void talk(){
System.out.println("说了一句话");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
子类部分:
package com.oop.demo05;
//Student也是Person
public class Student extends Person{
}
mian部分:
package com.oop;
import com.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.talk();//输出“说了一句话”
System.out.println(student.getMoney());//输出“1000000000”
}
}
在创建student对象后可以发现,Student类没有任何方法,但是student对象却能打点调用父类Person里的talk()方法
在Java中,所有的类,都默认直接或者间接的继承Object类
快捷键:alt+h可以查看类之间的层次关系:
会发现所有类都直接或间接继承Object类
7.1 super关键字
作用
- 在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造方法中调用父类的构造器
【注意】
- 尤其当子父类出现同名成员时,可以用super进行区分
- super的追溯不仅限于直接父类,super还可以调用父类的父类的属性方法
- super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
7.1.1 用super访问父类的属性
Person父类部分:
package com.oop.demo05;
public class Person{
protected String name = "liam";
}
Student子类部分:
package com.oop.demo05;
public class Student extends Person{
private String name = "sprite";
public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
main部分:
package com.oop;
import com.oop.demo05.Person;
import com.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("LIAM");
}
}
输出结果:
7.1.2 用super调用父类的方法
Person父类部分:
package com.oop.demo05;
public class Person{
private String name;
public Person() {
System.out.println("我是父类的无参构造");
}
public Person(String name) {
System.out.println("我是父类的有参构造");
this.name = name;
}
}
Student子类部分:
package com.oop.demo05;
public class Student extends Person{
private String name;
public Student() {
super();//可以省略
System.out.println("我是子类的无参构造");
}
public Student(String name) {
//super(name); //-------(3)
super();//可以省略
System.out.println("我是子类的有参构造");
this.name = name;
}
}
main部分:
package com.oop;
import com.oop.demo05.Person;
import com.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student1 = new Student(); //-------(1)
System.out.println("==================");
Student student2 = new Student("LX"); //-------(2)
}
}
输出结果:
(1)语句执行的是子类的无参数构造方法,内部默认有super(),代表执行父类无参数构造方法,因此输出父类无参数构造方法中的语句和子类无参数构造方法中的语句;
(2)语句执行的是子类有参数构造方法,内部也是默认有super(),代表执行父类无参数构造方法,,输出语句是父类无参数构造方法中的语句和子类有参数构造方法中的语句;
若将(3)语句解除屏蔽,则子类有参构造方法中执行super("LX")表示执行父类有参构造方法Person("LX"),修改后Student子类:
package com.oop.demo05;
public class Student extends Person{
private String name;
public Student() {
super();//可以省略
System.out.println("我是子类的无参构造");
}
public Student(String name) {
super(name); //-------(3)
//super();
System.out.println("我是子类的有参构造");
this.name = name;
}
}
输出结果:
对比可得:子类构造器内默认调用父类的无参构造器,只有用super("LX")主动调用父类有参构造才会调用父类有参构造
7.1.3 super()代表子类调用父类的构造方法(在main方法创建一个对象是用new关键字来调用类的构造方法)
Person父类部分:
package com.oop.demo05;
public class Person{
public Person() {
System.out.println("Person无参执行了");
}
}
Student子类部分:
package com.oop.demo05;
public class Student extends Person{
public Student() {
super();//隐藏的代码,不写也可以,代表调用了父类的无参构造
System.out.println("Student无参执行了");
}
}
main部分:
package com.oop;
import com.oop.demo05.Person;
import com.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
}
}
输出结果:
结果表明:在new了student对象时,会先调用父类Person类的构造器,这表示有一个默认的super()被调用了
【注意】
- super调用父类的构造方法必须在构造方法的第一行,不然会报错,如图所示
- super只能出现在子类的方法或者构造方法中
- this()表示调用本类的构造方法,super()和this()不能同时调用构造方法,因为this()和super()都要在第一行,如图所示
8 方法重写
8.1 重写的作用
在子类中创建一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这
种方式被称为方法重写(override),又称为方法覆盖。当父类中的方法无法满足子类需求或子类具有特有功能的时候,需要方法重写。
子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。
创建重写方法的快捷键:alt+insert选中override再选中要重写的方法即可
在重写方法时,需要遵循下面的规则:
- 重写都是方法的重写,与属性无关
- 参数列表必须完全与被重写的方法参数列表相同
- 返回的类型必须与被重写的方法的返回类型相同(Java1.5 版本之前返回值类型必须一样,之后的 Java 版本放宽了限制,返回值类型必须小于或者等于父类方法的返回值类型)
- 访问权限不能比父类中被重写方法的访问权限更低(public > protected > default > private)
- 重写方法一定不能抛出新的检查异常或者比被重写方法声明更加宽泛的检查型异常。例如,父类的一个方法声明了一个检查异常IOException,在重写这个方法时就不能抛出 Exception,只能拋出 IOException 的子类异常,可以抛出非检査异常。
另外还需要注意以下几条
- 重写的方法可以使用@override注解来标识
- 父类的成员方法只能被它的子类重写
- 声明为final的方法不能被重写
- 声明为static的方法不能被重写,但是能再次声明
- 构造方法不能被重写
- 子类和父类在同一个包中时,子类可以重写父类的所有方法,除了声明为privae和final的方法
- 子类和父类不在同一个包中时,子类只能重写父类的声明为public和protected的非final方法
- 如果不能继承一个方法,则不能重写这个方法
8.2 重写案例
父类Animal类:
package com.oop.demo05;
public class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void getInfo(){
System.out.println("宠物名字是"+name+",年龄是"+age);
}
}
子类Cat类:
package com.oop.demo05;
public class Cat extends Animal{
private String hobby;
public Cat(String name, int age, String hobby) {
super(name, age);
this.hobby = hobby;
}
@Override//注解作用,起到功能注释的作用
public void getInfo() {
System.out.println("猫猫的名字是"+name+",年龄是"+age+",爱好是"+hobby);
}
}
main方法:
package com.oop;
import com.oop.demo05.Animal;
import com.oop.demo05.Cat;
public class Application {
public static void main(String[] args) {
Animal animal = new Cat("富贵",3,"吃鱼");
animal.getInfo();
}
}
输出结果:
-
上述代码,在 Animal 类中定义了一个getInfo() 的方法,而 Cat 类继承自该类,因此 Cat 类同样含有与 Animal 类中相同的 getInfo() 方法。但是我们在 Cat 类中又重新定义了一个 getInfo() 方法,即重写了父类中的 getInfo() 方法。子类重写了父类的方法,执行子类的方法。
-
如果子类Cat中有一个方法eat(),父类没有此方法,那么
Animal animal = new Cat("富贵",3,"吃鱼");
中的animal无法调用eat()方法。对象能执行哪些方法,主要看对象左边的类型,和右边类型不大。
9 多态
9.1 多态概念
-
多态是面向对象程序设计的一个重要特征,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
-
可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
-
比如,你的女盆友让你买点水果回来,不管买回来的是苹果还是西瓜,只要是水果就行,这个就是生活中多态的体现
-
再比如,小猫、小狗、小猪我们可以把他们都归纳成小动物,每种小动物都需要吃东西,所以我们可以统一设置他们都必须吃,但是每种小动物的习性不一样,那这个就可以设置成小动物自己特有的功能,多态对象只能调用父类中定义子类中重写的功能,并不能调用子类的特有功能,这样就实现了代码的统一
9.2 多态特点
- 多态的前提1:是继承
- 多态的前提2:要有方法的重写
- 父类引用指向子类对象,如:Animal a = new Cat();//创建一个Cat实例对象a,但a是Animal类型
- 多态中,编译看左边,运行看右边
9.3 多态案例
父类Animal类:
package com.oop.demo05;
public class Animal {
//创建父类普通方法
public void eat(){
System.out.println("动物吃啥都行");
}
}
子类Cat类:
package com.oop.demo05;
public class Cat extends Animal{
//添加重写方法
@Override
public void eat() {
System.out.println("猫猫爱吃鱼");
}
//添加子类特有方法
public void jump(){
System.out.println("猫猫跳的老高了");
}
}
子类Dog类:
package com.oop.demo05;
public class Dog extends Animal{
//添加重写方法
@Override
public void eat() {
System.out.println("狗狗爱吃肉");
}
//添加子类特有方法
public void run(){
System.out.println("狗狗跑的老快了");
}
}
main方法:
- 创建”纯纯的“对象用于测试
package com.oop;
import com.oop.demo05.Animal;
import com.oop.demo05.Cat;
import com.oop.demo05.Dog;
public class Application {
public static void main(String[] args) {
//1.创建”纯纯的“对象用于测试
Animal a = new Animal();
Cat c = new Cat();
Dog d = new Dog();
a.eat();//动物吃啥都行,调用的是父类自己的功能
c.eat();//猫猫爱吃鱼,调用的是子类重写后的功能
d.eat();//狗狗爱吃肉,调用的是子类重写后的功能
//a.jump();//报错,Animal类里并没有这个方法
//a.run();//报错,Animal类里并没有这个方法
c.jump();//猫猫跳的老高了,子类可以调用自己的功能
d.run();//狗狗跑的老快了,子类可以调用自己的功能
}
}
- 创建多态对象用于测试
package com.oop;
import com.oop.demo05.Animal;
import com.oop.demo05.Cat;
import com.oop.demo05.Dog;
public class Application {
public static void main(String[] args) {
//2.创建多态对象进行测试
/*口诀1:父类引用指向子类对象
* 解释:创建出来的子类对象的地址值,交给父类类型的引用类型变量来保存*/
Animal a2 = new Cat();//Cat类对象的地址值交给父类型变量a2来保存
Animal a3 = new Dog();//Dog类对象的地址值交给父类型变量a3来保存
/*口诀2:编译看左边,运行看右边
* 解释:必须要在父类定义这个方法,才能通过编译,把多态对象看作是父类类型
* 必须要在子类重写这个方法,才能满足多态,实际干活的是子类*/
a2.eat();//猫猫爱吃鱼,多态对象使用的是父类的定义,子类的方法体
//a2.jump();//无法调用子类Cat的jump方法
}
}
9.4 多态的好处
- 多态可以让我们不用关心某个对象到底具体是什么类型,就可以使用该对象的某些方法
- 提高了程序的可扩展性和可维护性
9.5 多态的使用
前提:多态对象把自己看做是父类类型
- 成员变量:使用的是父类的
- 成员方法:由于存在重写现象,所以使用的是子类的
- 静态成员:随着类的加载而加载,谁调用就返回谁的,静态方法属于类资源,不存在重写现象,静态方法在哪个类定义,就作为哪个类的资源使用(final和private也不存在重写现象)
- 口诀1:父类引用指向子类对象
- 口诀2:编译保存(指各个属性参数)看左边,运行效果(指方法)看右边,意思就是成员变量用的是父类的,方法用的是子类的
9.6 多态成员使用测试
父类Animal类:
package com.oop.demo05;
public class Animal {
//创建父类成员变量
public int sum =10;
//创建父类普通方法
public void eat(){
System.out.println("吃啥都行");
}
//定义父类的静态方法play
public static void play(){
System.out.println("玩啥都行");
}
}
子类Dog类:
package com.oop.demo05;
public class Dog extends Animal{
//创建子类的成员变量
public int sum = 20;
//重写父类方法
@Override
public void eat() {
System.out.println("狗狗爱吃肉");
}
//创建子类静态方法play
/*这不是一个重写的方法,只是恰巧在两个类中出现了一模一样的两个静态方法
静态方法属于类资源,只有一份,不存在重写的现象
在哪个类定义,就作为哪个类的资源使用
*/
public static void play(){
System.out.println("狗狗爱玩球");
}
}
main方法:
- 创建”纯纯的“对象用于测试
package com.oop;
import com.oop.demo05.Animal;
import com.oop.demo05.Dog;
public class Application {
public static void main(String[] args) {
//创建”纯纯的“子类对象
Dog dog = new Dog();
System.out.println(dog.sum);//20,子类自己的属性
dog.eat();//狗狗爱吃肉,子类自己的方法
dog.play();//狗狗爱玩球,并没有重写父类静态方法
}
}
- 创建多态对象用于测试
package com.oop;
import com.oop.demo05.Animal;
import com.oop.demo05.Dog;
public class Application {
public static void main(String[] args) {
/*口诀1:父类引用指向子类对象*/
/*口诀2:编译(保存)看左边,运行(效果)看右边*/
Animal a = new Dog();
System.out.println(a.sum);//10,多态中,方法的声明使用的是父类的,方法体使用的是子类的
a.eat();//狗狗爱吃肉
/*多态中,调用的静态方法是父类的,因为多态对象把自己看作是父类类型
* 直接使用父类中的静态资源*/
a.play();//玩啥都行
Animal.play();//玩啥都行
}
}
9.7 instanceof关键字
用途
Java 中的instanceof运算符是用来指出对象是否是特定类的一个实例,返回值是Boolean。
public class Application {
public static void main(String[] args) {
Animal a = new Dog();//创建一个Dog实例对象a,但a是Animal类型
System.out.println(a instanceof Animal);//true,a是Dog实例对象,属于Animal类
System.out.println(a instanceof Dog);//true,a是Dog实例对象,属于Dog类
System.out.println(a instanceof Object);//true,a是Dog实例对象,属于Object类
}
}
9.8 向上转型和向下转型
在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。
那么在这个过程中就存在着多态的应用。存在着两种转型方式,分别是:向上转型和向下转型。
- 向上转型,即低转高:可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
-
比如:父类Person,有walk方法,子类Student,有study方法
父类的引用指向子类对象,将低一级的Student类型转化为高一级的Person类型,不用强转:
Person p=new Student();
//创建一个Student实例对象p,但p是Person类型说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类中声明过的方法,方法体执行的就是子类重过后的功能。但是此时对象是把自己看做是父类类型的,所以其他资源使用的还是父类型的。子类转换为父类可能会丢失自己本来的一些方法。
public class Application { public static void main(String[] args) { /* 把Person p = new Student分为Student s = new Student();和Person p = s;两句更容易理解低转高 * */ Student s = new Student(); //s.walk();//转化前可以执行 //s.study();//转化前可以执行 Person p = s; p.walk();//转化后可以执行 //p.study//转化后不可执行 } }
比如:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。
花木兰只能调用父类方法(从军),或者调用重写了父类中声明过的方法(替他爸从军),无法调用自己独特的方法(贴花黄)
- 向下转型,即高转低(较少):子类的引用的指向子类对象,过程中必须要采取到强制转型。这个是之前向上造型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象
-
Person p=new Student();
//向上转型,此时p是Person类型Student s = (Student)p
//此时把高一级的Person类型p转成低一级的Student类型就相当于创建了一个子类对象一样,可以调用父类方法也能调用自己的方法
说明:向下转型是为了方便调用子类的专有方法,可以减少重复的代码,不用为了调用子类方法而再重新创建另一个子类对象来调用。例:
Person p=new Student();
,此时想调用子类Student的专有方法study()
,就不用创建另一个子类的引用指向子类对象Student s = new Student();
,直接Student s = (Student)p;
强转一下就可以使用子类的专有方法了。比如:花木兰打仗结束后,就不需要再看作是她爸了,就可以”对镜贴花黄“了
10 static总结
10.1 static修饰属性
静态变量和实例变量的区别:
-
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
-
在程序运行时的区别:
-
实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
-
静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
-
总之,实例变量从属于对象,静态变量从属于类,静态变量能够被所有的实例对象共享,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
-
10.2 static修饰方法
静态方法跟类一起加载,普通方法跟对象一起加载,与static修饰属性的情况一样。
如Student类有普通方法a()和静态方法b(),a能直接调用b,但b不能直接调用a,要想再b调用a,需要先创建一个Student实例对象,再用对象调用a。
10.3 static修饰代码块
package com.oop.demo06;
public class Student {
//代码块可以赋初始值
//第二个执行
{
System.out.println("匿名代码块");
}
//静态代码块只执行一次
//第一个执行
static {
System.out.println("静态代码块");
}
//第三个执行
public Student() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Student student = new Student();
//输出结果:
//静态代码块
//匿名代码块
//构造方法
}
}
10.4 通过static静态导入包
package com.oop.demo06;
//静态导入包
import static java.lang.Math.random;
public class Student {
public static void main(String[] args) {
System.out.println(random());//静态导入Math.random后可以不用Math.random(),可以省略Math
}
}
11 抽象类
抽象类是单继承,接口可以多继承(比如插座,约束了可以插什么头,但是没约束可以用什么电器)
父类抽象类:
package com.oop.demo06;
public abstract class Action {
public abstract void doSomething();//抽象方法,只有方法名字,没有方法的实现
}
子类:
package com.oop.demo06;
//继承了抽象类的子类方法必须要重写父类的方法来实现功能
public class A extends Action{
@Override
public void doSomething() {
System.out.println("do something");
}
}
抽象的特点
- 抽象类不能new出来,只能靠new抽象类的子类来调用方法
- 抽象类可以有抽象方法,这些抽象方法要此抽象类的子类来实现;抽象类也可以有一些正常的方法
- 抽象方法必须在抽象类里,抽象方法里可以有抽象方法也可以有普通方法
抽象的意义
比如游戏创建新角色,创建新角色的发色、穿着可以重写子类方法即可;可以节省代码,说白了还是为了提高开发效率。
12 接口
12.1 接口的概念
- 官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
- 通俗解释:接口可以理解为一种特殊的类,里面全部是由*全局常量*和公共的抽象方法所组成。接口是解决*Java无法使用多继承*的一种手段,但是接口在实际中更多的作用是*制定标准*的。或者我们可以直接把接口理解为*100%的抽象类*,既接口中的方法*必须全部*是抽象方法。(JDK1.8之前可以这样理解)
- 普通类只有具体实现,抽象类具体实现和规范(即抽象方法)都有,接口只有规范(即抽象方法)
12.2 接口的特点
就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(*即只有方法标识符,而没有方法体*)。
eg.
UserService接口:
package com.oop.demo07;
//interface定义的关键字,接口都需要有实现类,实现类名字一般后缀加Impl
public interface UserService {
//接口中所有定义都是抽象的public
int AGE = 99;//常量默认有public static final
void add();//省略了public abstract,默认是有public abstract的
void delete();
void update();
void query();
}
Timer接口:
package com.oop.demo07;
public interface Timer {
void timer();
}
UserServiceImpl实现类(实现类一般后缀加Impl):
package com.oop.demo07;
//利用imlements关键字实现接口具体功能,实现类可以实现多个接口的功能比如下面还实现了Timer的方法
public class UserServiceImpl implements UserService,Timer{
//实现类必须要重写接口中的方法,以此实现具体功能
@Override
public void add() {
//具体实现方法
}
@Override
public void delete() {
//具体实现方法
}
@Override
public void update() {
//具体实现方法
}
@Override
public void query() {
//具体实现方法
}
@Override
public void timer() {
//具体实现方法
}
}
13 内部类
内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
13.1 内部类的分类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
13.2 成员内部类
内部类可以获取外部类的私有属性和私有方法,这是普通类做不到的。
外部类Outer和内部类Inner:
package com.oop.demo08;
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
//内部类Inner
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//内部类可以获取外部类的私有属性
public void getID(){
System.out.println(id);
}
}
}
//普通类
class method{
public void getID(){
//System.out.println(id);//报错,普通类无法获取其他普通类的私有属性
}
}
调用内部类的方法:
main方法部分:
package com.oop;
import com.oop.demo08.Outer;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.in();//输出“这是内部类的方法”
}
}
13.3 静态内部类
静态内部类无法获取外部类的属性,除非外部类的属性也是静态属性。因为静态成员是随类最先一起加载的。
package com.oop.demo08;
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
public static class Inner{
public void getID(){
//System.out.println(id);//报错,静态内部类无法获取外部类的属性,除非外部类的属性也是静态属性
}
}
}
13.4 局部内部类
定义在方法中的内部类
package com.oop.demo08;
public class Outer {
public void method(){
//局部内部类
class Inner{
public void in(){
}
}
}
}
13.5 匿名内部类
package com.oop.demo08;
public class Test {
public static void main(String[] args) {
//没有名字的初始化类,不用将实例保存到变量中
new Apple().eat();
}
}
class Apple{
public void eat(){
System.out.println("吃苹果");
}
}
标签:子类,面向对象,第五章,oop,Student,父类,com,public 来源: https://www.cnblogs.com/Ayisprite/p/16131455.html