Java面向对象
面向过程&面向对象
- 面向过程
步骤清晰简单,第一步做什么,第二步做什么…
面向过程适合处理一些较为简单的问题
-
面向对象
物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考,最后,才对某个分类下的细节进行面向过程的思索
面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
-
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统,但是,具体到微观操作,仍然需要面向过程的思路去处理
什么是面向对象
-
面向对象编程(Object-Oriented Programming,OOP)
-
面向对象编程的本质就是:以类的方式组织代码,以对象的形式组织(封装)数据
-
抽象:编程的最重要的思想
-
三大特性:
封装
继承
多态
- 从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
- 从代码运行角度考虑是先有类后有对象,类是对象的模板
类与对象的关系
-
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物
动物、植物、手机、电脑…
Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
-
对象是抽象概念的具体实例
张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例
能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念
package com.keke.oop.demo01;
/**
* @ClassName: Student
* @Description: 学生类
* @Author: keke
* @Date: 2021/4/7
*/
public class Student {
//属性:字段
String name; // 默认null
int age; // 默认0
//方法
public void study() {
System.out.println(this.name + "在学习");
}
}
package com.keke.oop.demo01;
/**
* @ClassName: Application
* @Description: 一个项目应该只存在一个main方法
* @Author: keke
* @Date: 2021/4/7
*/
public class Application {
public static void main(String[] args) {
//类:抽象的,实例化
//student对象就是一个Student类的具体实例
Student student = new Student();
Student xiaoming = new Student();
Student xiaohong = new Student();
xiaoming.name = "小明";
xiaoming.age = 3;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
xiaohong.name = "小红";
xiaohong.age = 3;
System.out.println(xiaohong.name);
System.out.println(xiaohong.age);
}
}
创建与初始化对象
-
使用new关键字创建对象
-
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的,并且构造器有以下两个特点:
1.构造方法的名称必须和所在的类名称完全一样,大小写也要一样
2.构造方法不要写返回值类型,连void都不写
3.构造方法不能return一个具体的返回值
4.如果没有编写任何构造,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。
5.一旦编写了至少一个构造方法,那么编译器不再赠送
6.构造方法也是可以重载的
-
构造器必须掌握
package cn.itcast.day06.demo03; import org.w3c.dom.ls.LSOutput; /* * 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法 * 格式: * public 类名称(参数类型 参数名称,...){ * 方法体 * } * 注: * 1.构造方法的名称必须和所在的类名称完全一样,大小写也要一样 * 2.构造方法不要写返回值类型,连void都不写 * 3.构造方法不能return一个具体的返回值 * 4.如果没有编写任何构造,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。 * 5.一旦编写了至少一个构造方法,那么编译器不再赠送 * 6.构造方法也是可以重载的 * 重载:方法名称相同,参数列表不同 * */ public class Student2 { //成员变量 private String name; private int age; //无参构造方法 public Student2() { System.out.println("无参构造方法执行啦!"); } //如果没有定义这个构造方法,系统将自动定义一个public Student2() {} //全参构造方法 public Student2(String name,int age){ System.out.println("全参构造方法执行啦!"); this.name = name; this.age = age; } //Setter,Getter public void setName(String name) { this.name = name ; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } }
局部变量和成员变量
package cn.itcast.day06.demo02;
/*
* 局部变量和成员变量(类变量)
* 1.定义位置不一样【重点】
* 局部变量:在方法内部
* 成员变量:在方法外部,直接写在类当中
*
* 2.作用范围不一样【重点】
* 局部变量:只有在方法中使用,出了方法不能用
* 成员变量:整个类全部可以通用
*
* 3.默认值不一样【重点】
* 局部变量:没有默认值,如果要使用必须手动赋值
* 成员变量:如果没有赋值,会有默认值,规则和数组一样
*
* 4.内存位置不一样【了解】
* 局部变量:位于栈内存
* 成员变量:位于堆内存
*
* 5.生命周期不一样【了解】
* 局部变量:随着方法进栈而诞生,随着方法出栈而消失
* 成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失
* */
public class Demo04VariableDifference {
String name; //成员变量
public void methodA(){
int num = 20; //局部变量
System.out.println(num);
System.out.println(name);
}
public void methodB(int param){ //方法的参数就是局部变量
//参数在方法调用的时候,必然会被赋值的
System.out.println(param);
int age; //局部变量
// System.out.println(age); //没复制不能用
// System.out.println(num); //错误写法
System.out.println(name);
}
}
Java简单内存图
代码:
package com.keke.oop.demo03;
/**
* @ClassName: Pet
* @Description:
* @Author: keke
* @Date: 2021/4/7
*/
public class Pet {
public String name;
public int age;
public void shout() {
System.out.println("叫了一声");
}
}
package com.keke.oop.demo03;
/**
* @ClassName: Application
* @Description:
* @Author: keke
* @Date: 2021/4/7
*/
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
Pet cat = new Pet();
}
}
内存图:
简单小结类和对象
1.类与对象
类是一个模板:抽象,对象是一个实例:具体
2.方法
定义、调用
3.对应的引用
基本类型(8种) 引用类型:除了8大基本类型
对象是通过引用操作的:栈—>堆(地址)
4.属性:即字段Field/成员变量
默认初始化:
数字:0 0.0
char:u0000
boolean:false
引用:null
定义:修饰符 属性类型 属性名 = 属性值
5.对象的创建和使用
- 必须使用new关键字创建对象(调用构造器) Person keke = new Person()
- 对象的属性 keke.name
- 对象的方法 keke.sleep()
6.类
一个标准的类通常拥有下面四个组成部分:
1.若有成员变量都要使用private关键字修饰
2.为每一个成员变量编写一对Setter/Getter方法
3.编写一个无参数的构造方法
4.编写一个全参数的构造方法
这样标准的类也叫做Java Bean
封装
- 该露的露,该藏的藏
我们程序设计要追求”高内聚,低耦合“。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉,低耦合:仅暴露少量的方法
给外部使用
-
封装(数据的隐藏)
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
-
记住这句话就行了:属性私有,get/set
package com.keke.oop.demo04;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
public class Student {
//属性私有
//名字
private String name;
//学号
private int id;
//性别
private char sex;
//年龄
private int age;
//提供一些可以操作这个属性的方法
//即提供一些public的get、set方法
//get:获得这个数据
public String getName() {
return this.name;
}
//set:给这个数据设置值
public void setName(String name) {
this.name = name;
}
//alt + insert:自动生成set和get
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 120 || age < 0) {
this.age = 3; //不合法的年龄都等于3岁
} else {
this.age = age;
}
}
}
package com.keke.oop.demo04;
/**
* @ClassName: Application
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
public class Application {
public static void main(String[] args) {
/*
* 总结:封装的意义
* 1.提高程序的安全性,保护数据
* 2.隐藏代码的实现细节
* 3.统一接口
* 4.系统可维护性增加了
* */
Student s1 = new Student();
s1.setName("keke");
System.out.println(s1.getName());
s1.setAge(999); //不合法,可以在set方法中设置规则
System.out.println(s1.getAge());
}
}
继承
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
-
extends的意思是”扩展“,子类是父类的扩展
-
Java中类只有单继承,没有多继承,即一个儿子只能有一个爸爸,但一个爸爸可以有多个儿子
-
继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)子类继承父类,使用关键字extends来表示
-
子类和父类之间,从意义上讲应该具有”is a“的关系
-
在Java中,所有的类都默认直接或者间接继承Object
-
final关键字修饰的类不可以被继承,断子绝孙
-
object类
-
super关键字
-
方法重写
父类:Person
package com.keke.oop.demo05;
/**
* @ClassName: Person
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
// Person 人:父类
// 在Java中,所有的类都默认直接或者间接继承Object
public class Person {
/*
* public则所有的子类以及其它类都可以使用
* 若换成private则其子类以及其它类均不能使用
* 若换成protected则可以被与父类在不同包下的子类和同一包下的其他类使用
* 若换成default(即不写也不能写)则可以被同与父类在同一包下的子类以及其它类使用
* */
protected int money = 10_0000_0000;
public void say() {
System.out.println("说了一句话");
}
}
子类:Student、Teacher
package com.keke.oop.demo05;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
// Student is 人:子类 / 派生类
// 子类继承父类,就会拥有父类的全部方法
public class Student extends Person {
// ctrl + h:查看本类的继承关系
}
package com.keke.oop.demo05;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
//Teacher is 人:子类 / 派生类
public class Teacher extends Person {
}
测试类:Application
package com.keke.oop.demo05;
/**
* @ClassName: Application
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.money);
Person person = new Person();
int a = person.money;
}
}
super关键字
首先需要了解不同访问修饰符修饰属性、方法带来的效果:
public:所有的子类以及其它类都可以使用
private:其子类以及其它类均不能使用
protected:可以被与父类在不同包下的子类和同一包下的子类以及其他类使用
default(即不写也不能写):可以被同与父类在同一包下的子类以及其它类使用
具有可见:https://blog.csdn.net/qq_46601365/article/details/115561447
1、一个类创建默认含有一个无参构造方法,若显示一个有参构造方法,无参构造方法将被覆盖
2、使用super关键字调用构造方法只能被本类的构造方法调用,不能被本类的成员方法以及静态方法调用
3、super只能出现在子类的方法或者构造方法中
4、子类无参构造方法和有参构造方法在使用Alt+ins创建过程中都会提示调用父类的构造方法,且必须调用父类的构造方法,若选父类的无参构造,不显示,默认在第一行,显示也必须在第一行;若选父类的有参构造,显示,默认在第一行。有且只能有一个。即有子必有父
5、子类构造方法不能同时调用父类和自己的构造方法,即super和this不能同时调用构造方法,即子类构造方法不能调用自己的构造方法和构造方法
VS this :“通过谁调用的方法,谁就是this”
代表对象不同:this:本身调用者这个对象 super:父类对象的应用
前提:this:没有继承也可以使用 super:只能在继承条件才可以使用
构造方法:this():调用本类的构造方法 super():调用父类的构造方法
总结
super关键字的用法有三种:
1、在子类的成员方法中,访问父类的成员变量。
2、在子类的成员方法中,访问父类的成员方法。
3、在子类的构造方法中,访问父类的构造方法。
super关键字用来访问父类内容,而this关键字用来访问本类内容,用法有三种:
1、在本类的成员方法中,访问本类的成员变量
2、在本类的成员方法中,访问本类的另一个成员方法
3、在本类的构造方法中,访问本类的另一个构造方法
在第三种用法当中要注意:
1、this(…)也必须是构造方法的第一个语句,唯一一个
2、super和this两种构造调用,不能同时使用
父类
package com.keke.oop.demo06;
/**
* @ClassName: Person
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
// Person 人:父类
public class Person {
protected String name = "keke";
//注意:私有的东西无法被继承
public void print() {
System.out.println("Person");
}
public Person() {
}
public Person(String name) {
//this();
this.name = name;
System.out.println("Person无参执行了");
}
}
子类
package com.keke.oop.demo06;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
// Student is 人:子类 / 派生类
// 子类继承父类,就会拥有父类的全部方法
public class Student extends Person {
private String name = "qingjiang";
public void test(String name) {
System.out.println(name); //柯柯
System.out.println(this.name); //qingjiang
System.out.println(super.name); //keke
}
/*public void print() {
System.out.println("Student");
}*/
public void test1() {
print(); //Student
this.print(); //Student
super.print(); //Person
// super(); //Call to 'super()' must be first statement in constructor body:构造方法只能被本类的构造方法调用,且必须在第一行
}
public Student() {
//隐藏代码:调用了父类的无参构造
//注意:如果显示super(),调用父类的无参构造,必须写在最前面,否则会报错
// super();
//子类无参构造方法不能同时调用父类和自己的构造方法,即super和this不能同时调用构造方法,即子类无参构造方法不能调用自己的无参构造方法和有参构造方法
// this();
//父类不含无参构造时,子类构造必须显示调用父类的有参构造
// super("aaa");
System.out.println("Student无参执行了");
}
public Student(String name, String name1) {
super(name);
this.name = name1;
}
public Student(String name) {
//隐藏代码:调用了父类的无参构造
//注意:如果显示super(),调用父类的无参构造,必须写在最前面,否则会报错
// super();
//子类无参构造方法不能同时调用父类和自己的构造方法,即super和this不能同时调用构造方法,即子类无参构造方法不能调用自己的无参构造方法和有参构造方法
// this();
//父类不含无参构造时,子类构造必须显示调用父类的有参构造
super("aaa");
this.name = name;
System.out.println("Student有参执行了");
}
}
package com.keke.oop.demo06;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
//Teacher is 人:子类 / 派生类
public class Teacher extends Person {
}
测试类
package com.keke.oop.demo06;
/**
* @ClassName: Application
* @Description:
* @Author: keke
* @Date: 2021/4/8
*/
public class Application {
public static void main(String[] args) {
Person person = new Person("aaa");
person.print();
// student.test("柯柯");
// student.test1();
}
}
方法重写
重写前提:
1、需要有继承关系,子类重写父类的方法
2、方法名必须相同
3、参数列表必须相同
4、修饰符:范围可以扩大但不能缩小:public>protected>default>private
5、抛出的异常:范围可以缩小但不能扩大:例:ClassNotFoundException --> Exception(大)
注意点:
1、重写都是方法的重写,与属性无关
2、重写只针对非静态方法,static、final、private修饰的方法无法重写
3、子类的方法和父类必须一致,方法体不同!
为什么需要重写:
父类的功能,子类不一定需要,或者不一定满足
父类
package com.keke.oop.demo07;
/**
* @ClassName: B
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
//重写都是方法的重写,与属性无关
public class B {
public void test() {
System.out.println("B=>test()");
}
public static void testStatic() {
System.out.println("B=>testStatic()");
}
}
子类
package com.keke.oop.demo07;
/**
* @ClassName: A
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class A extends B {
//alt + ins:选择override:重写父类的方法
@Override //注解:有功能的注释! Override:重写
public void test() {
// super.test(); //默认调用父类的方法
System.out.println("A=>test"); //重写自己的代码
}
public static void testStatic() {
System.out.println("A=>testStatic()");
}
}
测试类
package com.keke.oop.demo07;
/**
* @ClassName: Application
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Application {
public static void main(String[] args) {
A.testStatic(); //A=>testStatic()
B.testStatic(); //B=>testStatic()
A a = new A();
a.test(); //A=>test()
//父类的引用指向子类对象
B b = new A();
b.test(); //A=>test()
/*
* 结论:重写只针对非静态方法
* */
}
}
成员变量
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
直接通过子类对象访问成员变量:
等号左边是谁,就优先用谁,没有则向上找
间接通过成员方法访问成员变量:
该方法属于谁,就用谁,没有则向上找
注意:当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量
如果需要访问本类当中的成员变量,需要使用格式:this.成员变量名
父类
package cn.itcast.day09.demo02;
public class Fu {
int numFu = 10;
int num = 100;
public void methodFu() {
//使用本类当中的,不会向下找子类
System.out.println(num);
}
}
子类
package cn.itcast.day09.demo02;
public class Zi extends Fu {
int numZi = 20;
int num = 200;
public void methodZi() {
int num = 300; //局部变量
//因为本类当中有num,所以这里用的是本类的num
System.out.println(num); //访问局部变量
System.out.println(this.num); //访问类变量
}
}
测试类
package cn.itcast.day09.demo02;
/*
* 在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
*
* 直接通过子类对象访问成员变量:
* 等号左边是谁,就优先用谁,没有则向上找
* 间接通过成员方法访问成员变量:
* 该方法属于谁,就用谁,没有则向上找
* */
public class Demo02ExtendsField {
public static void main(String[] args) {
Fu fu = new Fu();
System.out.println(fu.numFu); //只能使用父类的东西,没有任何子类的内容
System.out.println("===================");
Zi zi = new Zi();
System.out.println(zi.numFu); //10
System.out.println(zi.numZi); //20
System.out.println("===================");
//等号左边是谁,就优先用谁
System.out.println(zi.num); //优先子类,200
// System.out.println(zi.abc); //到处都没有,编译报错!
System.out.println("===================");
//这个方法是子类的,优先用子类的,没有再向上找
zi.methodZi(); //200
//这个方法是在父类当中定义的
zi.methodFu(); //100
}
}
多态
-
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
-
一个对象的实际类型是确定,但可以指向对象的引用的类型有很多
-
多态存在的条件
有继承 / 实现关系
子类重写父类 / 实现接口的方法
父类引用指向子类 / 接口引用指向实现类对象
注意:继承和实现都可以使用多态,意思其实是一样的,下面主要讨论继承实现多态的特性
-
注意:多态是方法的多态,属性没有多态
-
instanceof
-
总结:
-
对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
例:父类的引用指向子类的对象后,若调用子类特有的方法则报错,但可以强制转换为子类对象再调用
-
父类的引用指向子类的对象后,该对象调用重写的方法执行的是子类重写后的方法
-
补充:
-
编译时的多态:方法重载(overload)
-
运行时的多态:方法重写(override)
-
运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
父类
package com.keke.oop.demo08;
/**
* @ClassName: Person
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Person {
public void run() {
System.out.println("run");
}
}
子类
package com.keke.oop.demo08;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Student extends Person {
@Override
public void run() {
System.out.println("son");
}
public void eat() {
System.out.println("eat");
}
}
测试类
package com.keke.oop.demo08;
/**
* @ClassName: Application
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的
// new Student();
// new Person();
//但是可以指向的引用类型就不确定了
//Student能调用的方法都是自己或者继承父类的
Student s1 = new Student();
//Person 父类型 可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student(); //父类的引用指向子类的对象
Object s3 = new Student();
s2.run(); //子类重写了父类的方法,执行子类的方法
s1.run();
//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
// s2.eat(); //报错
((Student) s2).eat(); //强制转换后可以调用
s1.eat();
}
}
访问成员变量
两种方式:
1.直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找
2.间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找
父类
package cn.itcast.day10.demo10;
public class Fu /*extends object*/ {
int num = 10;
public void showNum() {
System.out.println(num);
}
public void method() {
System.out.println("父类方法");
}
public void methodFu() {
System.out.println("父类特有方法");
}
}
子类
package cn.itcast.day10.demo10;
public class Zi extends Fu{
int num = 20;
int age = 16;
@Override
public void showNum() {
System.out.println(num);
}
@Override
public void method() {
System.out.println("子类方法");
}
public void methodZi() {
System.out.println("子类特有方法");
}
}
测试类
package cn.itcast.day10.demo10;
/*
* 访问成员变量的两种方式:
* 1.直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找
* 2.间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找
* */
public class Demo01MultiField {
public static void main(String[] args) {
//使用多态的写法,父类引用指向子类对象
Fu obj = new Zi();
System.out.println(obj.num); //父:10
// System.out.println(obj.age); //错误写法
System.out.println("=================");
//子类没有覆盖重写,就是父:10
//子类如果覆盖重写,就是子:20
obj.showNum();
}
}
访问成员方法
在多态的代码中,成员方法的访问规则是:
-
看new的是谁就优先用谁,没有则向上找
-
口诀:编译看左边,运行看右边
-
对比一下:
-
成员变量:编译看左边,运行还看左边
-
成员方法:编译看左边,运行看右边
-
总结:访问成员变量时,优先父类,若父类无,则报错
-
访问成员方法时,优先看子类,若子类重写了父类的方法,直接调用子类的方法,否则,调用父类
-
子类特有的成员变量和成员方法,无法被访问,但可以通过类型转换,见引用类型转换
package cn.itcast.day10.demo10;
/*
* 在多态的代码中,成员方法的访问规则是:
* 看new的是谁就优先用谁,没有则向上找
* 口诀:编译看左边,运行看右边
*
* 对比一下:
* 成员变量:编译看左边,运行还看左边
* 成员方法:编译看左边,运行看右边
*
* 总结:访问成员变量时,优先父类,若父类无,则报错
* 访问成员方法时,优先看子类,若子类重写了父类的方法,直接调用子类的方法,否则,调用父类
* 子类特有的成员变量和成员方法,无法被访问
* 注意:继承和实现都可以使用多态
* */
public class Demo02MultiMethod {
public static void main(String[] args) {
Fu obj = new Zi();
obj.method(); //优先用子
obj.methodFu(); //子类没有,父类有,向上找到父类
//访问子类特有的属性
int age = ((Zi) obj).age;
System.out.println(age); //10
//编译看左边,左边是Fu,Fu当中没有methodZi方法,所以编译报错
// obj.methodZi(); //错误写法
}
}
instanceof
instanceof的作用:判断一个对象是什么类型,用于引用类型的判断
**X instanceof Y: **
是否能编译通过:取决于X和Y是否存在父子关系,存在则编译通过,不存在则编译失败
是否为true:取决于X指向的对象类型是不是Y的子类型或者Y类型本身
父类
package com.keke.oop.demo09;
/**
* @ClassName: Person
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Person {
public void run() {
System.out.println("run");
}
}
子类
package com.keke.oop.demo09;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Student extends Person {
}
package com.keke.oop.demo09;
/**
* @ClassName: Teacher
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Teacher extends Person {
}
测试类
package com.keke.oop.demo09;
/**
* @ClassName: Teacher
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Application {
public static void main(String[] args) {
//Object > String
//Object > Person > Teacher
//Object > Person > Student
Object object = new Student();
System.out.println(object instanceof Student); //true
System.out.println(object instanceof Person); //true
System.out.println(object instanceof Object); //true
System.out.println(object instanceof Teacher); //false
System.out.println(object instanceof String); //false
System.out.println("==================================");
Person person = new Student();
System.out.println(person instanceof Student); //true
System.out.println(person instanceof Person); //true
System.out.println(person instanceof Object); //true
System.out.println(person instanceof Teacher); //false
// System.out.println(person instanceof String); //编译报错
System.out.println("==================================");
Student student = new Student();
System.out.println(student instanceof Student); //true
System.out.println(student instanceof Person); //true
System.out.println(student instanceof Object); //true
// System.out.println(student instanceof Teacher); //编译报错
// System.out.println(student instanceof String); //编译报错
System.out.println("==================================");
Person person1 = new Person();
System.out.println(person1 instanceof Student); //false
System.out.println(person1 instanceof Person); //true
System.out.println(person1 instanceof Object); //true
System.out.println(person1 instanceof Teacher); //false
// System.out.println(person1 instanceof String); //false
}
/*
* System.out.println(X instanceof Y);
* 是否能编译通过:取决于X和Y是否存在父子关系,存在则编译通过,不存在则编译失败
* 是否为true:取决于X指向的对象类型是不是Y的子类型或者Y类型本身
* */
}
引用类型转换
1、引用类型之间的转换:就是父类与子类之间的转换
2、前提条件:父类引用指向子类对象
-
向下转型(强制转换):父 -> 子
-
向上转型:子 -> 父,会丢失一些子类特有的方法
3、作用:方便方法的调用,减少重复的代码
父类
package com.keke.oop.demo10;
/**
* @ClassName: Person
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Person {
public void run() {
System.out.println("run");
}
}
子类
package com.keke.oop.demo10;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Student extends Person {
int num = 10;
public void go() {
System.out.println("go");
}
}
测试类
package com.keke.oop.demo10;
/**
* @ClassName: Teacher
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Application {
public static void main(String[] args) {
//引用类型之间的转换:父 子
//高 -> 低
Person obj = new Student();
// obj.go(); //父类引用指向子类对象:无法调用子类特有的方法
int num = ((Student) obj).num; //访问子类特有的属性
System.out.println(num); //10
//将obj这个对象转换为Student类型,就可以使用Student类特有的方法
//向下转型
((Student) obj).go();
//子类转换为父类,可能会丢失自己本来的一些方法
Student student = new Student();
student.go();
Person person = student; // 低 -> 高 向上转换
// person.go(); //报错
}
}
练习
简单模拟电脑工作
USB接口类
package cn.itcast.day10.demo12;
public interface USB {
public abstract void Open(); //打开设备
public abstract void close(); //关闭设备
}
鼠标类
package cn.itcast.day10.demo12;
//鼠标就是一种USB设备
public class Mouse implements USB {
@Override
public void Open() {
System.out.println("打开鼠标");
}
@Override
public void close() {
System.out.println("关闭鼠标");
}
public void click() {
System.out.println("鼠标点击");
}
}
键盘类
package cn.itcast.day10.demo12;
//键盘是一个USB设备
public class Keyboard implements USB {
@Override
public void Open() {
System.out.println("打开键盘");
}
@Override
public void close() {
System.out.println("关闭键盘");
}
public void type() {
System.out.println("键盘输入");
}
}
电脑类
package cn.itcast.day10.demo12;
public class Computer {
public void powerOn() {
System.out.println("笔记本电脑开机");
}
public void powerOff() {
System.out.println("笔记本电脑关机");
}
//使用USB设备的方法,使用接口作为方法的参数
public void useDevice(USB usb) {
usb.Open(); //打开设备
if(usb instanceof Mouse) { // 一定要先判断
Mouse mouse = (Mouse) usb;
mouse.click();
}else if(usb instanceof Keyboard){ // 一定要先判断
Keyboard keyboard = (Keyboard) usb;
keyboard.type();
}
usb.close(); //关闭设备
}
}
测试类
package cn.itcast.day10.demo12;
public class DemoMain {
public static void main(String[] args) {
//首先创建一个笔记本电脑
Computer computer = new Computer();
computer.powerOn();
//准备一个鼠标,供电脑使用
// Mouse mouse = new Mouse();
//首先进行向上转型
USB usbMouse = new Mouse(); //多态写法
//参数是USB类型,正好传递进去的就是USB鼠标
computer.useDevice(usbMouse);
//创建一个USB键盘
Keyboard keyboard = new Keyboard(); //没有使用多态写法
//方法参数是USB类型,传递进去的是实现类对象
computer.useDevice(keyboard); //正确写法,也发生了向上转型
//使用子类对象,匿名对象,也可以
// computer.useDevice(new Keyboard()); //也是正确写法
computer.powerOff();
System.out.println("=================");
method(10.0); //正确写法,double --> double 10.0
method(20); //正确写法,int --> double 20.0
int a = 30;
method(a); //正确写法,int --> double 30.0
}
public static void method(double num) {
System.out.println(num);
}
}
static关键字
- 作用:
1、修饰类中的成员变量 -> 类变量:可以通过类名访问,不需要new一个对象
2、修饰类中的成员方法(非静态方法)-> 静态方法:可以通过类名访问,不需要new一个对象
package com.keke.oop.demo11;
/**
* @ClassName: Student
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Student {
private static int age; //静态变量
private double score; //成员变量 / 非静态变量
public void run() {
// go(); //成员方法中可以直接调用静态方法
} //成员方法 / 非静态方法
public static void go() {
// run(); //报错 静态方法中不能直接调用成员方法
// Student student = new Student();
// student.run();
} //静态方法
public static void main(String[] args) {
Student s1 = new Student();
// System.out.println(s1.age);
System.out.println(Student.age); //推荐使用类名调用
System.out.println(s1.score);
// System.out.println(Student.score); //报错
new Student().run();
Student.go();
go(); //因为在本类中调用,可以省略类名
}
}
3、静态代码块:类加载即执行,只执行一次
package com.keke.oop.demo11;
/**
* @ClassName: Person
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Person {
//2
{
//匿名代码块(不建议):没有名字,程序在执行的时候不能主动调用,创建本类对象时自动创建,在构造器之前
//作用:赋初值
System.out.println("匿名代码块");
}
//1
static {
//静态代码块(建议):类加载即执行,只执行一次
System.out.println("静态代码块");
}
//3
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person = new Person(); //静态代码块 匿名代码块 构造方法
System.out.println("=================");
Person person1 = new Person(); //静态代码块 匿名代码块 构造方法
}
}
4、静态导入包:将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便
package com.keke.oop.demo11;
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
/**
* @ClassName: Test
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Test {
public static void main(String[] args) {
// System.out.println(Math.random());
System.out.println(random()); //使用静态导入包后,不用写Math,直接写random()方法即可
System.out.println(PI);
}
}
-
注意:
1、成员方法中可以直接调用静态方法,但是静态方法中不能直接调用成员方法,需要new一个对象通过对象调用使。因为用static关键字修饰的属性和方法在内存中是跟着类一起加载,所以可以通过类名直接调用
final关键字
final关键字代表最终的,不可改变的
常见四种用法:
1.可以用来修饰一个类
2.可以用来修饰一个方法
3.可以用来修饰一个局部变量
4.可以用来修饰一个成员变量
注意:
final修饰的变量叫做常量:必须初始化,否则不能使用;初始化后不能再改变值,不会变动的值
修饰一个类
package cn.itcast.day11.demo01;
/*
* 当final关键字用来修饰一个类的时候,格式:
* public final class 类名称() {
* //...
* }
*
* 含义:当前这个类不能有任何子类。(太监类)
* 注:一个类如果是final类,那么其中所有的成员方法都无法进行覆盖重写。(因为没儿子)
* */
public final class MyClass {
public void method() {
System.out.println("方法执行");
}
}
package cn.itcast.day11.demo01;
//不能使用一个final类作为父类
public class MySubClass /*extends MyClass*/ {
}
修饰一个方法
package cn.itcast.day11.demo01;
/*
* 当final关键字用来修饰一个方法时,这个方法就是最终方法,也就是不能被覆盖重写
* 格式:
* 修饰符 final 返回值类型 方法名称(参数类型) {
* //方法体
* }
*
* 注:
* 对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾
* */
public abstract class Fu {
public final void method() {
System.out.println("父类方法执行");
}
public abstract /*final*/ void methodAbS();
}
package cn.itcast.day11.demo01;
public class Zi extends Fu {
@Override
public void methodAbS() {
}
//错误写法,不能覆盖重写父类当中final的方法
// @Override
// public void method() {
// System.out.println("子类覆盖重写父类的方法");
// }
}
修饰一个局部变量
package cn.itcast.day11.demo01;
/*
* final关键字代表最终的,不可改变的
*
* 常见四种用法:
* 1.可以用来修饰一个类
* 2.可以用来修饰一个方法
* 3.可以用来修饰一个局部变量
* 4.可以用来修饰一个成员变量
* */
public class Demo01Final {
public static void main(String[] args) {
int num1 = 10;
System.out.println(num1); //10
num1 = 20;
System.out.println(num1); //20
//一旦使用final用来修饰局部变量,那么这个变量就不能被改变
//“一次使用,终生不变”
final int num2 = 200;
System.out.println(num2); //200
// num2 = 250; //错误写法,不能改变!
// num2 = 200; //错误写法
//正确写法,只要保证有唯一一次赋值即可
final int num;
// System.out.println(num); //报错,必须赋初值才能使用
num = 30;
//对于基本类型,不可变就是变量当中的数据不可变
//对于引用类型,不可变就是变量当中的地址值不可变
Student stu1 = new Student("赵丽颖");
System.out.println(stu1);
System.out.println(stu1.getName()); //赵丽颖
stu1 = new Student("霍建华");
System.out.println(stu1);
System.out.println(stu1.getName()); //霍建华
System.out.println("===============");
final Student stu2 = new Student("高圆圆");
//错误写法,final的引用变量,其中的地址值不可改变,但内容可以改变
// stu2 = new Student("赵又廷");
System.out.println(stu2.getName()); //高圆圆
stu2.setName("高圆圆圆圆圆圆");
System.out.println(stu2.getName()); //高圆圆圆圆圆圆
}
}
修饰一个成员变量
package cn.itcast.day11.demo01;
/*
* 对于成员变量来说,如果用final关键字修饰,那么这个变量也照样是不可变
* 1.由于成员变量具有默认值,所以用了final关键字之后必须手动赋值,不会再给默认值了
* 2.对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值,二者选其一
* 3.必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值
* */
public class Person {
private final String name/* = "鹿晗"*/;
public Person() {
name = "关晓彤"; //不写报错
}
public Person(String name) {
this.name = name; //不写报错
}
public String getName() {
return name;
}
// public void setName(String name) {
// this.name = name;
// }
}
抽象类
-
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类
-
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
-
抽象类,不能使用new关键字来创建对象,它用来让子类继承
-
抽象方法,只有方法的声明,没有方法的实现,它用来让子类实现
-
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
-
存在的意义:把一些类的公共方法给抽象定义在一起,然后创建这些类都继承抽象类,避免的这些类公共方法的重复编写,简化了代码的开发,提高开发效率和扩展性
-
能否称为架构师最重要的一点是是否具有抽象的思维!
抽象类
package com.keke.oop.demo12;
/**
* @ClassName: Action
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
//abstract修饰类代表是抽象类 类只能单继承,接口可以多继承
public abstract class Action {
//abstract,抽象方法,只有方法名字,没有方法的实现
public abstract void doSomething();
//特点:
//1.不能使用new关键字来创建对象,它用来让子类继承,相当于一个约束
//2.抽象类中可以写普通的方法
//3.抽象方法必须在抽象类中
public void hello() {
System.out.println("普通的方法");
}
}
子类
package com.keke.oop.demo12;
/**
* @ClassName: A
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
//子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
public class A extends Action {
@Override
public void doSomething() {
}
}
测试类
package com.keke.oop.demo12;
/**
* @ClassName: Application
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public class Application {
public static void main(String[] args) {
// new Action(); //报错
Action a = new A();
}
}
思考:抽象类不能new,那么它是否存在构造器?
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
public abstract class Action {
public Action() {
}
public abstract void doSomething();
public void hello() {
System.out.println("aaa");
}
}
解答:通过javac Action.java命令生成的Action.class文件可以看出抽象类存在构造器
接口
介绍
-
普通类:只有具体实现
-
抽象类:具体实现和规范(抽象方法)都有
-
接口:只有规范,自己无法写方法 专业的约束,实现约束和实现分离:也就是面向接口编程
-
接口就是规范,定义的是一组规则,体现了现实世界中,“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须干坏人;如果你是坏人,则必须欺负好人。
-
接口的本质就是契约,就像我们人间的法律一样,制定好后大家都遵守
-
OOP的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如:java、c++、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象
接口的作用:
1、接口本质是一个约束、规范
2、定义了一些方法,让不同的人是实现,比如十个类都是实现了一个接口,但是实现方式不同
3、接口中的所有定义的方法其实都是抽象的,默认自带public abstract
4、接口中的所有定义的属性都是静态常量,默认自带public static final,不能改变,但一般不在接口中定义属性
5、接口不能被实例化,因为接口中没有构造方法
6、一个类可以实现多个接口,使用implements关键字实现
7、实现接口必须重写接口中全部的抽象方法
Java8新特性(重要)
1、接口中可以定义静态方法(必须使用static修饰),不是抽象的,具体实现的,可以直接使用接口名称调用
2、接口中可以定义默认方法(必须使用default修饰),默认方法是非静态方法,非静态方法只能通过对象调用,但是接口是不能创建对象的名故而我们需要子类来实现接口
此外,值得注意的是:面向对象中,java只是支持单继承,不支持多继承,一个类只能继承一个类,但接口与接口之间是多继承的(例:public interface MyInterface extends MyInterfaceA,MyInterfaceB)
(1)如果多个接口定义了同样的静态方法,
即使存在实现类,同时实现多个接口,仍然是不可使用实现类名调用接口的方法
(2)如果多个接口定义了同样的默认方法
实现类实现多个接口时,必须重写掉默认方法,否则编译失败。
(3)如果一个类同时实现了接口和继承父类,父类中的默认方法(成员方法)和接口中的默认方法冲突
优先用父类当中的方法。
接口和抽象类的区别(重要)
注:抽象方法:必须被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
1、实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
2、接口中可以定义抽象方法(默认被public abstract修饰,不能修改)、[默认方法(必须使用default修饰)、静态方法](默认被public修饰,不能更改),可以定义属性但都是静态常量,默认自带public static final,不能改变,必须赋初值,不能实现类被修改;接口中常量的名称,使用完全大写的字母,用下划线进行分隔 (推荐命名规则)
抽象类中可以定义抽象方法、静态方法、成员方法、静态属性和非静态属性,抽象方法必须显示被abstract修饰。
3、抽象类的优点:如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个 新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点
接口的优点:一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。(使用抽象类,那么继承这个抽象类的子类类型就比较单一,因为子类只能单继承抽象类;而子类能够同时实现多个接口,因为类型就比较多。接口和抽象类都可以定义对象,但是只能用他们的具体实现类来进行实例化。) 从该点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。
4、结合3点中抽象类和Java接口的各自优势,具精典的设计模式就出来了:声明类型的工作仍然由Java接口承担,但是同时给出一个Java 抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择继承这个抽象类,也就是说在层次结构中,Java 接口在最上面,然后紧跟着抽象类,这下两个的最大优点都能发挥到极至了。这个模式就是“缺省适配模式”。在Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。(A extends AbstractB implements interfaceC,那么A即可以选择实现(@Override)接口interfaceC中的方法,也可以选择不实现;A即可以选择实现(@Override)抽象类AbstractB中的方法,也可以选择不实现)
缺省适配模式的好处:假设设计了一个接口,但实现这个接口的子类并不需要实现接口中的全部方法,也就是说,接口中的方法过多,对于某些子类是多余的,我们不得不浪费的写上一个空的实现;这时可以定义一个抽象类去实现该接口,对接口中所有的抽象方法进行空的实现,以便后续具体子类只需继承该抽象类按照需要重写自己想要的方法进行具体实现,另外,抽象类中还可以定义抽象方法,也可以实现对个接口,将抽象类和接口的优势发挥到了极致!
接口
package com.keke.oop.demo13;
/**
* @ClassName: TimeService
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
public interface TimeService {
void timer();
}
package com.keke.oop.demo13;
/**
* @ClassName: UserService
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
//interface 定义的关键字,接口都需要有实现类
public interface UserService {
//接口中的所有定义的属性都是静态常量,默认自带public static final,但一般不在接口中定义属性
int AGE = 99;
//接口中的所有定义的方法其实都是抽象的,默认自带public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
static void aaa() {};
}
抽象类
package com.keke.oop.demo13;
/**
* @ClassName: UserService
* @Description:
* @Author: keke
* @Date: 2021/4/10
*/
//类可以实现接口 implements 接口
//实现了接口的类就必须重写接口中所有的抽象方法
//可以利用接口实现多继承
public abstract class UserServiceAbstract implements UserService, TimeService{
@Override
public void timer() {
}
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
}
实现类
package com.keke.oop.demo13;
/**
* @ClassName: UserServiceImpl
* @Description:
* @Author: keke
* @Date: 2021/4/11
*/
public class UserServiceImpl extends UserServiceAbstract {
@Override
public void add(String name) {
System.out.println("add");
}
@Override
public void delete(String name) {
System.out.println("delete");
}
}
Java9新特性(了解)
问题描述:
我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码问题
但是这个共有方法不应该让实现类使用,应该是私有化的
解决方案:
从Java 9开始,接口当中允许定义私有方法
1.普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表) {
方法体
}
2.静态私有方法:解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表) {
方法体
}
接口含有普通私有方法
package cn.itcast.day10.demo05;
/*
* 问题描述:
* 我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码问题
* 但是这个共有方法不应该让实现类使用,应该是私有化的
*
* 解决方案:
* 从Java 9开始,接口当中允许定义私有方法
* 1.普通私有方法,解决多个默认方法之间重复代码问题
* 格式:
* private 返回值类型 方法名称(参数列表) {
* 方法体
* }
* 2.静态私有方法:解决多个静态方法之间重复代码问题
* 格式:
* private static 返回值类型 方法名称(参数列表) {
* 方法体
* }
* */
public interface MyInterfacePrivateA {
public default void methodDefault1() {
System.out.println("默认方法1");
methodCommon();
}
public default void methodDefault2() {
System.out.println("默认方法2");
methodCommon();
}
private void methodCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
实现类
package cn.itcast.day10.demo05;
public class MyInterfacePrivateAImpl implements MyInterfacePrivateA {
public void methodAnother() {
//直接访问到了接口中的普通私有方法,这样是错误的
// methodCommon();
methodDefault1();
methodDefault2();
}
}
接口含有静态私有方法
package cn.itcast.day10.demo05;
public interface MyInterfacePrivateB {
public static void methodStatic1() {
System.out.println("静态方法1");
methodStaticCommon();
}
public static void methodStatic2() {
System.out.println("静态方法2");
methodStaticCommon();
}
private static void methodStaticCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
测试类
package cn.itcast.day10.demo05;
public class Demo04Interface {
public static void main(String[] args) {
MyInterfacePrivateB.methodStatic1();
MyInterfacePrivateB.methodStatic2();
//错误写法
// MyInterfacePrivateB.methodStaticCommon();
}
}
内部类
- 内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就称为内部类,而A类相对于B类来说就称为外部类
1、成员内部类
2、静态内部类
3、局部内部类
4、匿名内部类
package com.keke.oop.demo14;
/**
* @ClassName: Outer
* @Description:
* @Author: keke
* @Date: 2021/4/11
*/
public class Outer {
private int id = 10;
private static int id2 = 20;
private void out() {
System.out.println("这是外部类的方法");
}
private static void out2() {
System.out.println("这是外部类的方法2");
}
public void test() {
System.out.println("用于测试匿名内部类的方法");
}
//成员内部类
public class Inner {
public void in() {
System.out.println("这是成员内部类的方法");
}
//获得外部类的私有属性、私有方法~
public void getID() {
System.out.println(id);
out();
System.out.println(id2);
out2();
}
}
//静态内部类
public static class Inner2 {
public void in() {
System.out.println("这是静态内部类的方法");
}
//只能获得外部类的静态私有属性、静态私有方法~,因为静态属性、方法、类会先实例化
public void getID() {
// System.out.println(id);
// out();
System.out.println(id2);
out2();
}
}
//局部内部类:写在外部类的方法中
public testInner3 method() {
class Inner3 implements testInner3 { //这里不能有访问修饰符
public void in() {
System.out.println("这是局部内部类的方法");
}
}
return new Inner3();
}
}
//一个Java类中可以有对个class类,但是只能有一个public class
/*public*/ class A {
public static void main(String[] args) {
}
}
interface testInner3 {
void in();
}
测试类
package com.keke.oop.demo14;
/**
* @ClassName: Applicaiton
* @Description:
* @Author: keke
* @Date: 2021/4/11
*/
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通过这个外部类来实例化内部类~
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
System.out.println("=======================");
//调用局部内部类的方法
testInner3 inner3 = outer.method();
inner3.in(); //这是局部内部类的方法
System.out.println("=======================");
//匿名内部类:没有名字初始化类,不用将实例保存到变量中
new Outer().test();
//常用于实现接口
new UserService() {
@Override
public void hello() {
}
};
}
}
interface UserService {
void hello();
}