一、继承 :extends
把多个类中相同的部分提取到另一个类中,然后让多个类继承自这个类。
继承优点: 提高了代码的复用性 多个类相同的成员可以放到同一个类中 提高了代码的维护性 如果功能的代码需要修改,修改一处即可 让类与类之间产生了关系,是多态的前提 其实这也是继承的一个弊端:类的耦合性很强
开发思想:低耦合,高内聚
java中的继承在使用上有哪些特点:
1、java中的继承不允许一个类同时继承多个类,只能单继承
2、java中的类支持多层继承,形成继承体系
java中的继承使用的注意事项:
1、子类只能继承父类中非私有的成员(成员变量或者成员方法)
2、继承关系中,子类无法继承父类的构造方法,父类构造方法最重要的意义是初始化子类对象之前,必须先对父类做初始化(要想有儿子,必须先有父亲)
3、不要为了部分的功能去使用继承
1、继承与成员变量的关系:
super:子类中要使用父类中的成员,可以使用super.的方式
class Fu{
int a =10;
}
class Zi extends Fu{
int a=20;
public void fun1(){
System.out.println(a);
//在子类中想要使用父类中的成员,可以使用super关键字
System.out.println(super.a);
}
}
public class Text1 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.fun1();
}
}
2、面试题:super关键字与this关键字的区别:
this的使用:
- 成员变量:this.成员变量名
- 构造方法:this(..)
- 成员方法:this.成员方法名(..)
super的使用:
- 成员变量:super.成员变量名
- 构造方法:super(..)
- 成员方法:super.成员方法名(..)
不同点: this在本类中使用 super关键字在子类中使用,代表子类中要使用父类的成员,super不能跨层使用,super只能代表直接父类的引用
三、java中的继承与构造方法的关系
子类中所有的构造方法默认都会访问父类中空参数的构造方法
为什么呢?因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。
每一个构造方法的第一条语句默认都是:super()
回顾上一个案例说的一个结论:定义的时候用构造方法进行初始化,要想初始化子类,必须先初始化父类
注意事项:
1、子类的构造方法中第一句话上面默认会有一个super(),super()的意义就是调用父类的无参构造方法
class Fu1{
Fu1(){
System.out.println("这是父类的构造方法");
}
}
class Zi1 extends Fu1{
//子类的构造方法中第一句话上面默认会有一个super()
Zi1(){
System.out.println("这是子类的构造方法");
}
}
public class Text2 {
public static void main(String[] args) {
Zi1 zi1 = new Zi1();
}
}
//这是父类的构造方法
//这是子类的构造方法
如果父类中没有无参数的构造方法,有有参数的构造方法,就会报错
![](https://img-blog.csdnimg.cn/6c672ee5e4af40468cedce2a33ec5cfa.png)
2、在此基础之上继续写,如果父类中没有无参构造方法怎么办?
1)解决方案
我们就在子类构造方法第一句话通过super去显示调用父类其他的带参的构造方法,完成父类的初始化
- 子类调用父类的构造方法,明着写调用带参数的构造方法,完成父类的初始化
- 先初始化父类,就要使用父类构造方法去初始化,但是父类没有无参的构造方法,
- 因此给父类的构造方法传值,使得父类初始化
2)解决方案2:子类通过this去调用本类中其他的构造方法,该构造方法要访问量父亲的构造方法。间接地调用父类中的其他构造方法,完成父类的初始化
class Fu1{
Fu1(String s){
System.out.println("这是父类的构造方法");
}
}
class Zi1 extends Fu1{
//子类的构造方法中第一句话上面默认会有一个super()
//利用子类中的其他构造方法
Zi1(String s){
super(s);
}
Zi1(){
//子类调用父类的构造方法,明着写调用带参数的构造方法,完成父类的初始化
//先初始化父类,就要使用父类构造方法去初始化,但是父类没有无参的构造方法,
//因此给父类的构造方法传值,使得父类初始化
//super("你好");
//调用当前对象的构造方法,传参
this("你好");
System.out.println("这是子类的构造方法");
}
}
public class Text2 {
public static void main(String[] args) {
Zi1 zi1 = new Zi1();
}
}
四、继承中成员方法的关系
同名和不同名的方法
- 通过子类对象去访问一个方法
- 首先在子类中找
- 然后在父类中找
如果还是没有就报错。(不考虑父亲的父亲…)
class Fu5{
public void fun1(){
System.out.println("这是父类中的fun1方法");
}
}
class Zi5 extends Fu5{
public void fun1(){
System.out.println("这是子类中的fun1方法");
}
public void fun2(){
fun1();//就近原则
}
}
public class ExtendsDemo5 {
public static void main(String[] args) {
Zi5 zi5 = new Zi5();
zi5.fun2();
}
}
五、方法的重写
方法重写的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
特点:如果方法名相同,最终使用的是子类自己的
方法重写:当子类中的方法声明(返回值类型,方法名,参数列表)与父类中的方法声明一样,这样的现象叫做方法的重写, 一般是需要在子类重写的方法上加一个注解@Override,表示该方法是重写方法
方法重写的注意事项
1、父类中私有方法不能被重写
2、子类重写父类方法时,访问权限不能更低
3、父类静态方法无法被重写
面试题:方法的重载与方法的重写是什么区别?
1、重载是发生在同一个类中的,而重写是发生在继承关系中。
2、重载是方法名一样,方法参数列表不一样就叫重载,和返回值类型无关
3、重写要求方法返回值类型,方法名,参数列表都要与父类中的一致,只有权限修饰符和方法体的实现不一样
class Fu2{
public void call(){
System.out.println("打电话");
}
}
class Zi2 extends Fu2{
//重写的方法
@Override
public void call(){
System.out.println("边打电话边玩游戏");
}
}
public class Text3 {
public static void main(String[] args) {
Zi2 zi2 = new Zi2();
zi2.call();
}
}
//边打电话边玩游戏
final:是最终的意思
可以修饰类,成员变量,成员方法
1、修饰类,类不能被继承
2、修饰变量,变量就变成了常量,只能被赋值一次
1)字面值常量
2)自定义常量:final所修饰的常量
3、修饰方法,方法不能被重写
5、在引用数据类型(比如类)之前加上final关键字,指的是引用数据类型变量的地址值不能被改变
class Fu3{
final int a=10;
}
class Zi3 extends Fu3{
public void fun(){
//被final所修饰的成员变量变成常量,值无法被改变
//a=20;
System.out.println(a);
}
}
public class Final {
int a=100;
public static void main(String[] args) {
// final Zi3 zi3 = new Zi3();
//zi3.fun();
//在引用数据类型之前加上final关键字,指的是引用数据类型变量的地址值不能被改变
final Final f = new Final();
f.a=100;
System.out.println(f.a);
}
}
6、final修饰变量的初始化时机 ,(赋值)在对象构造完毕前即可
不加final,系统会给一个默认值,加上final ,就必须在构造方法之前给对象赋值
class F{
final int a;
//2、构造代码块,可以在构造方法之前执行
{
a=30;
}
F(){
//1、在构造方法里面赋值
//a=20;
}
}
public class Final2 {
public static void main(String[] args) {
F f = new F();
System.out.println(f.a);
}
}
二、多态:一个事物表现出来的不同状态
猫是一种动物,狗是一种动物,不能反过来说动物是一种猫。
多态的写法:从右往左去读为猫是动物的一种
动物 d = new 猫();
动物 d2 = new 狗();
实现多态的条件:
1、必须要有继承关系
2、要有方法的重写,如果不重写语法上没问题,但是从意思上不合适
3、在创建对象的时候,要有父类的引用指向子类对象
class Animal{
public void fun(){
System.out.println("动物");
}
}
//继承关系
class Cat extends Animal{
//方法的重写
@Override
public void fun(){
System.out.println("🐱吃🐟");
}
}
public class DuoTai {
public static void main(String[] args) {
//父类的引用指向子类的对象
Animal animal=new Cat();
}
}
成员访问的特点:
成员变量 | 编译看左边(有没有),运行(结果)看左边 |
成员方法 | 编译看左边,运行看右边 |
静态方法 | 编译看左边,运行看左边 |
静态方法不能算方法的重写
因此子类特有的方法就无法使用。
多态使用的过程中出现的问题:无法使用子类特有的成员 怎么解决?
向下转型:
向下转型有一个前提:转型的类要和父类的引用是一个继承关系
转型之后,就可以使用子类的方法
格式:
Fu fu = new Zi();
Zi zi = (Zi) fu;
class Animal{
int a=10;
public void fun(){
System.out.println("动物");
}
public static void fun1(){
System.out.println("这是父类中的静态方法");
}
}
//继承关系
class Cat extends Animal{
int a=100;
//方法的重写
@Override
public void fun(){
System.out.println("🐱吃🐟");
}
//不是方法的重写,没有加@override
public static void fun1(){
System.out.println("这是子类中的静态方法");
}
public void fun2(){
System.out.println("这是子类特有的成员方法");
}
}
public class DuoTai {
public static void main(String[] args) {
//父类的引用指向子类的对象
Animal animal=new Cat();
System.out.println(animal.a);//10 成员变量:编译看左,运行看左
animal.fun1();//静态方法:编译看左,运行看左
//animal.fun2();//成员方法:编译看左,运行看右。animal中没有fun2()方法,编译不成功
//向下转型
Cat cat = (Cat) animal;
//现在可以使用fun2()方法了
cat.fun2();
}
}
曹操和曹植的故事(和三国故事情节无关,纯属个人瞎扯)
曹操和曹植是一对父子关系
class 曹操{
String name;
int age;public void ability(){
善于用兵
}
}class 曹植 extends 曹操{
public void ability(){
善于写诗
}public void playChess(){
善于下棋
}
}有一天,曹操有事卧病不起,突然敌人攻打城池,但是为了不降低士气,于是曹植想了一个办法:开始装自己父亲,替自己的父亲打仗
穿上父亲的衣服,粘上父亲的假胡子
曹操 c = new 曹植();
c.ability(); //在装父亲的期间,只能使用和父亲一样的方法,不能使用自己特有的方法
打完仗了,回到阵营,脱下衣服,撕掉胡子,做回自己
曹植 c2 = (曹植)c; //向下转型
c2.ability();
c2.playChess();
向下转型类型转换异常图:
多态的好处:
1、提高了程序的维护性(由继承保证)
2、提高了程序的扩展性(由多态保证)
3、多态的弊端: 不能访问子类特有功能, 那么我们如何才能访问子类的特有功能呢? 多态中的转型
抽象类:
抽象类的引入:java提供了一个机制给我们使用,如果将来要定义的类在现实生活中的意思是表达一个抽象的概念,在程序中写的时候应该将这个类定义成抽象类,也就意味着这个类将来没法创建对象(实例化)。
关键字:abstract
特点:
1、一个类加上了abstract关键字就变成了抽象类,抽象类不能被实例化(不能创建对象)。
2、抽象类中可以存在非抽象的方法,也可以存在抽象的方法,但是抽象的方法没有方法体。
3、如果一个类中存在抽象方法,这个类必须是抽象类。
4、如果一个具体的类继承自抽象类,必须重写抽象类中的所有抽象方法。
5、抽象类也可以继承抽象类,可以不去重写父抽象类中的抽象方法。
//抽象类
abstract class Animal{
//2、抽象类中可以存在非抽象的方法,也可以存在抽象的方法,但是抽象的方法没有方法体。
public void sleep(){
System.out.println("睡觉");
}
//3、如果一个类中存在抽象方法,这个类必须是抽象类。
//对于吃这个方法而言,也不应该有具体的实现,所以需要将这个方法也变成抽象方法,抽象方法没有方法体,连大括号都没有
public abstract void eat();
}
//具体的类继承抽象类
class Dog extends Animal{
//4、具体的类继承自抽象方法必须要重写该抽象类的方法
@Override
public void eat() {
System.out.println("🐕吃🥩");
}
}
//5、抽象类也可以继承抽象类,可以不去重写父抽象类中的抽象方法。
abstract class Demo extends Animal{
}
public class Abstract01 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
}
}
//🐕吃🥩
抽象类中成员的特点:
成员变量:可以是变量也可以是常量构造方法:可以存在构造方法,写构造方法的意义是为了今后初始化子类之前先初始化本类
成员方法:可以存在抽象方法也可以存在含有具体实现的方法
abstract class Demo2{
//成员变量可以是常量也可以是变量
int a;
final int b=20;
//有构造方法,目的是给子类初始化
Demo2(){
}
//成员方法可以是抽象方法也可以是具体的方法
public void fun1(){
System.out.println("吃");
}
public abstract void fun2();
}
class DemoZi extends Demo2{
//具体的类继承自抽象类必须要重写抽象类里的抽象方法
@Override
public void fun2() {
}
}
public class Abstrack02 {
public static void main(String[] args) {
}
}
抽象类练习
1、2、老师案例 :具体事物:十三期老师,十二期老师 共性:姓名,年龄,讲课
abstract class Teacher{
String name;
int age;
Teacher(){
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//讲课的内容我们不知道具体的,所以定义为抽象方法
public abstract void teach();
}
class TwentyTwo extends Teacher{
public TwentyTwo() {
}
public TwentyTwo(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("22期老师讲解Java");
}
}
class TwentyThree extends Teacher{
@Override
public void teach() {
System.out.println("23期老师讲解python");
}
}
public class AbstractText {
public static void main(String[] args) {
//创建一个22期老师
TwentyTwo twentyTwo = new TwentyTwo();
twentyTwo.teach();
//多态:由于父类是一个抽象类,因此叫抽象多态
Teacher teacher=new TwentyTwo("小虎",22);
teacher.teach();//编译看左,运行看右
}
}
2、假如我们在开发一个系统时需要对员工类进行设计
员工包含3个属性:姓名、工号以及工资。 经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。 请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
abstract class Staff{
String name;
int id;
int salary;
public Staff(String name, int id, int salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public abstract void work();
}
class Manage extends Staff{
int reward;
public Manage(String name, int id, int salary, int reward) {
super(name, id, salary);
this.reward = reward;
}
@Override
public void work() {
System.out.println("做ppt");
}
}
class Personnel extends Staff{
public Personnel(String name, int id, int salary) {
super(name, id, salary);
}
@Override
public void work() {
System.out.println("写代码");
}
}
public class AbstractText2 {
public static void main(String[] args) {
//使用多态,父类的引用指向子列类的对象
Staff staff = new Manage("张三", 001, 20000,2000);
staff.work();
}
}
1、一个类如果没有抽象方法,可不可以定义为抽象类?
如果可以,有什么意义?
可以。 有意义,表示现实生活中一个抽象概念的汇总,尽管类中没有其他内容,但是从逻辑上是有意义的。
2、abstract不能和哪些关键字共存
private 冲突 私有的,子类不能继承私有成员变量和方法,因为抽象方法本身就是为了今后具体的类继承抽象类去写的。
final 冲突 被finna修饰的方法不能被重写,但是抽象方法就是要被子类继承,所以冲突
static 冲突,静态方法的没法重写
接口:关键字 implements interface
接口:就是某一个事物本身的功能有限,想要进行扩展功能的时候可以使用接口。当然今后接口的具体使用,是由后面具体的类实现。
例如:有动物类,狗类继承自动物类,有些狗会骑自行车,但不是所有的狗都会骑自行车,所以定义一个接口,接口中有骑自行车的方法。所以java提供了一个机制给我们使用,当我们需要写扩展功能的时候,就可以定义一个接口 ,这里就可以将骑自行车定义成一个接口 ,将来哪些狗需要骑自行车,只需要实现这个接口即可。
1、定义一个接口,接口中有骑自行车方法
2、抽象父类:动物类Animal
3、普通的狗类:Dog
4、经过训练的狗类:SpecialDog
注意事项:
1、接口中定义的所有方法只能是 抽象方法 ,并且系统会默认在每一个方法前加上public abstract2、一个类要想实现接口,使用implements关键字进行实现
3、接口中只能存在常量,默认在变量的前面加上public static final
4、抽象类可以实现接口,但是可以不去实现抽象方法,也可以实现
5、一个具体的类实现接口,必须要实现接口中所有抽象方法
6、一个类可以继承一个类的同时实现多个接口,也可以直接实现多个接口
7、接口没有构造方法,它不能创建对象(不能实例化)
定义接口的语句格式 interface 接口名 { }
注意:接口名的命名方式与类名的命名方式是一样
就将接口看作是一个特殊的类,因为接口编译的时候也会被编译成一个class文件
interface Bike{
//3、接口中只能存在常量,默认在变量的前面加上public static final
int a=10;
//接口里面必须是抽象方法
void bike();
}
interface Sing{
void sing();
}
abstract class Animal2{
String name;
int age;
public abstract void eat();
}
class Dog2 extends Animal{
@Override
public void eat() {
System.out.println("🐕吃🥩");
}
}
//6、一个类可以继承一个类的同时实现多个接口,也可以直接实现多个接口
class SpecialDog extends Animal implements Bike,Sing{
//3、接口中只能存在常量,默认在变量的前面加上public static final
//a=100
int b=10;
@Override
public void eat() {
System.out.println("大口吃");
}
@Override
public void bike() {
System.out.println("骑自行车");
}
@Override
public void sing() {
System.out.println("唱歌");
}
}
public class Interface {
public static void main(String[] args) {
//因为是接口,会在变量前默认加上public static final 。加了static 属于类成员,所以我们可以通过接口名去调用
System.out.println(Bike.a);
//类里的成员变量要通过对象去调用
// System.out.println(SpecialDog.b);
//接口多态
Bike b1=new SpecialDog();
b1.bike();//编译看左,运行看右
}
}
类和类,类和抽象类,类和接口,接口和接口之间
1、具体的类和具体的类之间只能是继承关系,只能进行单继承,不能多继承,但是可以多重继承,形成继承体系 (继承 单继承 )
2、类和抽象类之间也只能是继承关系,也只能进行单继承,不能多继承,但是呢如果是一个具体的类继承了抽象类,就必须实现抽象类中所有抽象方法
3、类和接口之间是一个实现关系,一个类可以实现多个接口 (实现 单实现 多实现)
4、接口和接口之间是继承关系,并且允许多继承 (继承 单继承 多继承 )
面试题:请问java中允许多继承吗?回答这道题的时候,分别说明类和类之间,接口和接口之间的区别。
猫狗案例
,加入跳高的额外功能
猫: 姓名,年龄 吃
狗: 姓名,年龄 吃
会跳高的猫: 姓名,年龄 吃 跳高
会跳高的狗: 姓名,年龄 吃 跳高 ----------------------
动物类(抽象类): 姓名,年龄 吃(抽象方法)
跳高(接口): 跳高(抽象方法) 猫 extends
动物类: 重写吃方法 狗 extends 动物类: 重写吃方法
会跳高的猫 extends 动物类 implements 跳高: 重写吃方法 重写跳高方法
会跳高的狗 extends 动物类 implements 跳高: 重写吃方法 重写跳高方法
教练和运动员案例(学生分析然后讲解)
乒乓球运动员和篮球运动员。 乒乓球教练和篮球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所学知识: 分析,这个案例中有哪些抽象类,哪些接口,哪些具体类。
参数
形式参数
- 基本类型: 基本数据类型当作方法的参数的时候,传入的是具体的数值
- 引用类型:
具体的类:今后当你看到一个类作为方法形式参数类型的时候,说明要传入该类的对象 接口:今后当你看到一个接口作为方法形式参数类型的时候,说明要传入的是实现该接 口的子类对象。
抽象类:今后当你看到一个抽象类作为方法形式参数类型的时候,说明要传入的是继承 该抽象类的具体类的子类对象
数组:回顾前面的知识
返回值类型
- 基本类型:基本数据类型当作方法的返回值的,返回的是对应类的数值
- 引用类型
具体的类:今后当你看到一个类作为方法的返回值类型的时候,需要返回的是该类或者 是该类的子类对象
接口:今后当你看到一个接口作为方法的返回值类型的时候,需要返回的是实现该接 口的子类对象
抽象类:今后当你看到一个抽象类作为方法的返回值类型的时候,需要返回的是继承 该抽象类的具体子
数组
1、类作为方法形式参数类型,说明要传入该类的对象
class Student{
public void study(){
System.out.println("学习");
}
}
class StudentDemo{
//参数类型为一个类,则传入的值为该类的对象
public void show(Student student){
student.study();
}
}
public class ShuJu {
public static void main(String[] args) {
StudentDemo studentDemo = new StudentDemo();
//传入对象
// Student s = new Student();
// studentDemo.show(s);
studentDemo.show(new Student());
}
}
2、今后当你看到一个接口作为方法形式参数类型的时候,说明要传入的是实现该接 口的子类对象。
interface Sing1{
void fun1();
}
//实现该接口的类
class InterImpl implements Sing1{
@Override
public void fun1() {
System.out.println("唱的好听");
}
}
class Student2 {
//接口作为参数数据类型,传入实现该接口的类的对象
public void show(Sing1 sing1){
sing1.fun1();
}
}
public class CanShuDemo {
public static void main(String[] args) {
Student2 student2 = new Student2();
//传入对象
student2.show(new InterImpl());
}
}
3、今后当你看到一个抽象类作为方法形式参数类型的时候,说明要传入的是继承该抽象类的具体类的子类对象
abstract class People3{
public abstract void fun3();
}
class Student3 extends People3{
@Override
public void fun3() {
System.out.println("学习真好!!!");
}
}
class Student4{
//、今后当你看到一个抽象类作为方法形式参数类型的时候,
// 说明要传入的是继承该抽象类的具体类的子类对象
public void show(People3 people3){
people3.fun3();
}
}
public class CanShuDeno2 {
public static void main(String[] args) {
Student4 student4 = new Student4();
// 说明要传入的是继承该抽象类的具体类的子类对象
student4.show(new Student3());
}
}
4、返回值: 今后当你看到一个类作为方法的返回值类型的时候,需要返回的是该类或者 是该类的子类对象
class Teacher2{
public void fun1(){
System.out.println("好好学习,天天向上");
}
}
class Teacher2Demo{
//今后当你看到一个类作为方法的返回值类型的时候,需要返回的是该类或者是该类的子类对象
public Teacher2 show1(){
return new Teacher2();
}
}
public class FanhuiZhiDemo1 {
public static void main(String[] args) {
Teacher2Demo teacher2Demo = new Teacher2Demo();
Teacher2 teacher2 = teacher2Demo.show1();
teacher2.fun1();
}
5、今后当你看到一个接口作为方法的返回值类型的时候,需要返回的是实现该接口的子类对象
//接口
interface Inter5{
void show1();
}
//实现该接口的类
class Inter5Impl implements Inter5{
@Override
public void show1() {
System.out.println("好好学习,天天向上!");
}
}
class Teacher3{
//今后当你看到一个接口作为方法的返回值类型的时候,需要返回的是实现该接口的子类对象
public Inter5 fun1(){
return new Inter5Impl();
}
}
public class FanhuiZhiDemo2 {
public static void main(String[] args) {
Teacher3 teacher3 = new Teacher3();
Inter5 inter5 = teacher3.fun1(); // Inter5 inter5 = new Inter5Impl() 接口多态
inter5.show1();
}
}
6、今后当你看到一个抽象类作为方法的返回值类型的时候,需要返回的是继承该抽象类的具体子类对象
abstract class Teacher4{
public abstract void fun1();
}
//具体的类
class Teacher4Zi extends Teacher4{
@Override
public void fun1() {
System.out.println("今天是下雨天!!");
}
}
class Teacher4Demo{
//今后当你看到一个抽象类作为方法的返回值类型的时候,需要返回的是继承该抽象类的具体子类对象
public Teacher4 show1(){
return new Teacher4Zi();
}
}
public class FanhuiZhiDemo3 {
public static void main(String[] args) {
Teacher4Demo teacher4Demo = new Teacher4Demo();
Teacher4 teacher4 = teacher4Demo.show1(); // Teacher4 teacher4 = new Teacher4Zi() 抽象多态
teacher4.fun1();
System.out.println("====================");
new Teacher4Demo().show1().fun1(); //当调用方法后继续调用方法,这种写代码的方式叫做链式编程 后面在scala语言中常见
}
}
导包
概述 不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。
导包格式 import 包名;
注意: 这种方式导入是到类的名称。 虽然可以最后写*,但是不建议。
1、package 要写在Java文件的第一行
2、导包,在所有class之上和package之间写
3、使用别的包下的Demo类
import com.shujia.wyh.day08.ketang.bao1.Demo;
4、如果想要使用某个包下的所有类
import com.shujia.wyh.day08.ketang.bao1.*;
package com.shujia.wyh.day08.ketang;
//导包,在所有class之上和package之间写
//使用别的包下的Demo类
import com.shujia.wyh.day08.ketang.bao1.Demo;
import com.shujia.wyh.day08.ketang.bao1.Demo2;
//如果想要使用某个包下的所有类
import com.shujia.wyh.day08.ketang.bao1.*;
权限修饰符
public | protected | 默认 | private | |
同一类中 | √ | √ | √ | √ |
同一包子类,其他类 | √ | √ | √ | |
不同包子类 | √ | √ | ||
不同包其他类 | √ |
测试代码
public class Power{
public int a = 11;
protected int b = 22;
int c = 33;
private int d = 44;
public void fun1(){
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
类及其组成可以用的修饰符
类: 默认,public,final,abstract 我们自己定义:public居多
成员变量: 四种权限修饰符均可,final,static 我们自己定义:private居多
构造方法: 四种权限修饰符均可,其他不可 我们自己定义:public 居多
成员方法: 四种权限修饰符均可,fianl,static,abstract 我们自己定义:public居多
内部类:把类定义在其他类的内部,这个类就被称为内部类。
内部类的访问特点: 内部类可以直接访问外部类的成员,包括私有。 外部类要访问内部类的成员,必须创建对象。
按照内部类在类中定义的位置不同,可以分为如下两种格式:
1、成员位置(成员内部类):定义在类中方法外
2、局部位置(局部内部类):定在在类中方法中
一、成员内部类
1、创建对象的语句定义格式
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
2、成员内部类可以访问外部类中的所有成员(成员变量、成员方法)。
3、内部类可以加static,但是只能访问静态的成员。
3、外部类要想访问内部类的成员,就必须在本类中创建内部类的对象。格式为
Inner inner = new Inner();
inner.fun1();
4、其他的类要想访问内部类的成员,要创建该内部类的对象,格式为
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
Outer.Inner oi1=new Outer().new Inner();
内部类可以加static,但是只能访问静态的成员。如果该内部类为静态的,因为Inner为静态类,所有可以通过类名.的方式去获取
创建对象格式为
Outer.Inner oi2 = new Outer.Inner();
//成员内部类
class Outer{
int a=10;
static int b=20;
final int c=30;
static int d=40;
private void show1(){
System.out.println("你好");
}
//不产生单独的class文件
//内部类可以加static,只能访问静态的成员b
class Inner{
public void fun1(){
System.out.println(a);
System.out.println(b);
System.out.println(c);
//1、成员内部类可以访问外部类中的所有成员(成员变量、成员方法)
System.out.println(d);
show1();
}
}
//3、外部类的成员方法想要使用内部类的成员,就要创建内部类的对象
public void show2(){
Inner inner = new Inner();
inner.fun1();
}
}
public class InnerDemo {
public static void main(String[] args) {
//外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
// Outer.Inner oi1=new Outer().new Inner();
//2、内部类使用内部类的方法
// oi1.fun1();
//2、外部类的方法使用内部类的方法
Outer outer = new Outer();
outer.show2();
// 如何在其他类中,使用内部类的成员
//1、创建内部类的对象,然后调用
//2、如果内部类是静态的,那么传统的创建内部类的语句失效,那该怎么办?
//Outer.Inner oi2 = new Outer.Inner();因为Inner为静态类,所有可以通过类名.的方式去获取
//oi2.fun;
}
}
内部类被静态修饰后的方法有静态和非静态之分。他们的访问和不用静态是不一样的。 访问非静态方法:外部类名.内部类名 对象名 = new 外部类名.内部类名(); 访问静态方法:上面创建的对象访问,或者外部类名.内部类名.方法名();
成员内部类面试题:在控制分别输出:30,20,10
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println();
System.out.println();
System.out.println();
}
class Outer2 {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num); // 30
System.out.println(this.num); // 20
System.out.println(Outer2.this.num); // 10 Outer里面的this
}
}
public void fun1(){
int num = 100;
System.out.println(this.num);
}
}
public class Test1 {
public static void main(String[] args) {
Outer2.Inner oi2 = new Outer2().new Inner();
oi2.show();
}
}
二、局部内部类
将类定义在成员方法中的类
1、在jdk1.8之后,局部内部类中使用的成员方法中的变量在编译的时候默认会加上final关键字
2、访问内部类的成员:在该方法里面创建内部类的对象,调用内部类的方法。然后在main方法里创建外部类的对象调用fun1方法,就可以访问到局部内部类的成员
class Outer3{
int a=10;
public void fun1(){
int b=20;
//定义内部类
class Inner3{
// a=40;//在jdk1.8之后,局部内部类中使用的成员方法中的变量在编译的时候默认会加上final关键字
public void show1(){
System.out.println(a);
}
}
//在方法里面创建内部类的对象,然后创建外部类的对象调用fun1方法,就可以访问到局部内部类的成员
Inner3 inner3 = new Inner3();
inner3.show1();
}
}
public class InnerDemo2 {
public static void main(String[] args) {
Outer3 outer3 = new Outer3();
outer3.fun1();
}
}
匿名内部类 没有名字的类
前提:存在一个类或者接口 ,这里的类可以是具体类也可以是抽象类。
当抽象类作为形式参数数据类型,需要传入该抽象类的子类的对象·,因此需要自己去写一个类继承这个抽象类,然后创建这个类的对象。但是用了抽象类就不要自己去写类继承抽象类然后创建对象
语句定义格式:传入该抽象类子类的对象的地方,填上下面的代码。
用这些代码去取代对象,不要再创建别的class文件了
new 类(抽象类)/接口 () {
重写方法; }
/**
new 类(抽象类)/接口 (){
重写方法
}
当抽象类作为形式参数数据类型,需要传入该抽象类的子类的对象·,因此需要
自己去写一个类继承这个抽象类,然后创建这个类的对象。
但是用了抽象类就不要自己去写类继承抽象类然后创建对象
*/
abstract class Student5{
public abstract void study();
}
class StudentDemo5{
public void fun1(Student5 student5){
student5.study();
}
}
public class NiMingDemo {
public static void main(String[] args) {
StudentDemo5 studentDemo5 = new StudentDemo5();
//匿名内部类
studentDemo5.fun1(new Student5() {
@Override
public void study() {
System.out.println("我爱唱歌");
}
});
}
}
匿名内部类面试题
按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
interface Inner{
void show();
}
class Outer6{
public static Inner method(){
//return 后面应该是实现该接口类的对象
//一般要定义一个类去实现这个接口然后创建对象
//此时用匿名类去代替创建,就不要再产生别的class文件了
return new Inner() {
@Override
public void show() {
System.out.println("helloworld");
}
};
}
}
public class NiMingDemo2 {
public static void main(String[] args) {
//Outer可以直接.调用,所以method方法是静态的
//Outer.method()通过.调用show()方法,只有对象才能调用方法,
// 说明返回的是一个对象,
//又发现show方法和接口中的方法一样,说明该对象可以调用接口中的方法,得出返回值类型是接口
Outer6.method().show();
}
}