Java-进阶篇【面向对象进阶,static、单例、代码块、继承、权限修饰符、抽象类、接口】---04
1:PIC内容
2:static 关键字 修饰成员变量 【静态成员变量 静态成员方法】
static 是静态的意思,可以用来修饰 成员变量、成员方法。
static 修饰成员变量 之后称为静态成员变量(类变量),修饰方法之后称为静态方法(类方法)。【表示该成员变量在内存中只存在一份,可以被共享访问修改】
静态成员变量(有static 修饰,属于类,内存中加载一次)(如表示在线人数,需要被共享的信息,可以被共享访问)
static 修饰后的成员变量,可以被类的所有对象共享(访问、修改)
public class User static String name; int age; }
类名.静态成员变量 (推荐这样直接访问)
对象.静态成员变量 (也可以这样访问,但是不推荐)
成员变量【无 static 修饰,存在于每个对象中】,比如常用的 name,age 等都是属于每个对象的信息 【实例成员变量】
package com.itheima.d1_static; public class User { /** 在线人数。 注意:static修饰的成员变量:静态成员变量,只在内存中有一份,可以被共享【不会放在对象中】 */ public static int onlineNumber = 161; /** 实例成员变量:无static修饰,属于每个对象的,必须用对象名.访问 */ private String name; private int age; public static void main(String[] args) { // 目标:理解static修饰成员变量的作用和访问特点。 // 1、类名.静态成员变量。 System.out.println(User.onlineNumber); // 2、对象名.实例成员变量 // System.out.println(User.name); // 报错 User u = new User(); u.name = "张三"; u.age = 21; System.out.println(u.name); System.out.println(u.age); u.onlineNumber++; // 新来了一个人 System.out.println(u.onlineNumber); User u2 = new User(); u2.name = "张三2"; u2.age = 22; System.out.println(u2.name); System.out.println(u2.age); u2.onlineNumber++; // 新来了一个人 System.out.println(u.onlineNumber); System.out.println(User.onlineNumber); // 推荐方式 // 注意:同一个类中静态成员变量的访问可以省略类名。【其他类中访问需要加上类名】 System.out.println(onlineNumber); } }
static修饰的成员变量内存机制
3:static 关键字 修饰成员方法 【静态成员变量 静态成员方法】
成员方法的分类:
静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问。
实例成员方法(无static修饰,归属于对象),只能用对象触发访问
表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
如果该方法是以执行一个共用功能为目的,则可以申明成静态方法
package com.itheima.d1_static; public class Student { private String name; // 实例成员变量:无static修饰,属于对象。 public static int getMax(int age1, int age2){ // 静态成员方法: 有static修饰,归属于类,可以被共享访问,用类名或者对象名都可以访问。 return age1 > age2 ? age1 : age2; } public void study(){ // 例方法:属于对象的,只能用对象触发访问 System.out.println(name + "在好好学习,天天向上~"); } public static void main(String[] args) { // 1、类名.静态成员方法 System.out.println(Student.getMax(10, 3)); // 注意:同一个类中,访问静态方法,类名可以省略不写。 System.out.println(getMax(10, 32)); // study(); // 报错了 属于对象的,只能对象访问 // 2、对象.实例方法 Student s = new Student(); s.name = "猪八戒"; s.study(); // 3、对象.静态方法 (语法是可行,但是不推荐) System.out.println(s.getMax(13,34)); } }
4:static 修饰成员方法的内存原理
5:static 注意事项
static访问注意事项:
静态方法只能访问静态的成员,不可以直接访问实例成员。【静态成员属于类,可以共享,实例成员属于对象,对象触发】
实例方法可以访问静态的成员,也可以访问实例成员。【静态成员共享的】
静态方法中是不可以出现this关键字的 【this代表当前对象,静态方法可能不是对象调用的,所以不能出现this】
package com.itheima.d1_static; public class Test3 { /** 静态成员 */ public static int onlineNumber = 10; public static void test2(){ System.out.println("==test2=="); } /** 实例成员 */ private String name; public void run(){ System.out.println(name + "跑的快~~"); } // 3、静态方法中不可以出现this关键字 public static void test3(){ // System.out.println(this); // this只能代表当前对象!! } // 2、实例方法可以访问静态成员,也可以访问实例成员 public void go(){ System.out.println(Test3.onlineNumber); System.out.println(onlineNumber); test2(); System.out.println(name); System.out.println(this); run(); } // 1、静态方法只能访问静态成员,不能"直接"访问实例成员。 public static void test(){ System.out.println(Test3.onlineNumber); System.out.println(onlineNumber); test2(); } public static void main(String[] args) { // 目标:理解static 访问相关的语法:面试笔试题,或者以后理解程序很重要的知识(拓展)。 } }
6:static 工具类
工具类是什么?
类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的。
案例导学:
在企业的管理系统中,通常需要在一个系统的很多业务处使用 验证码 进行防刷新等安全控制
问题:
同一个功能多处开发,会出现代码重复度过高
使用工具类的好处
一是调用方便,二是提高了代码复用(一次编写,处处可用)
package com.itheima.d2_static_util; import java.util.Random; /** 工具类 */ public class ItheimUtil { /** 注意:由于工具类无需创建对象,所以把其构造器私有化会显得很专业! */ private ItheimUtil(){ } /** 静态方法。 */ public static String createVerifyCode(int n){ // 开发一个验证码: // 1、定义一个变量记住验证码。 String code = ""; // 2、定义一个变量记住全部验证码字符。 String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 3、定义一个循环生成几个随机索引,去得到几个字符 Random r = new Random(); for (int i = 0; i < n; i++) { // 4、获取随机索引对应的字符。链接给code int index = r.nextInt(data.length()); code += data.charAt(index); } return code; } public static void main(String[] args) { System.out.println(ItheimUtil.createVerifyCode(10)); } }
为什么工具类中的方法不用实例方法做? 实例方法需要创建对象调用。 此时用对象只是为了调用方法,这样只会浪费内存。 工具类定义时的其他要求: 由于工具里面都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有
private ItheimUtil
定义数组工具类 需求:在实际开发中,经常会遇到一些数组使用的工具类。请按照如下要求编写一个数组的工具类:ArraysUtils 1:我们知道数组对象直接输出的时候是输出对象的地址的,而项目中很多地方都需要返回数组的内容,
请在ArraysUtils中提供一个工具类方法toString,用于返回整数数组的内容,
返回的字符串格式如:[10, 20, 50, 34, 100](只考虑整数数组,且只考虑一维数组) 2:经常需要统计平均值,平均值为去掉最低分和最高分后的分值,请提供这样一个工具方法getAerage,用于返回平均分。
(只考虑浮点型数组,且只考虑一维数组) 3:定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果
package com.itheima.d2_static_util; public class ArrayUtil { private ArrayUtil(){ // 构造器私有化 } // 工具方法 public static String toSting(int[] arr){ if (arr == null) { return null; } // 拼接内容返回 String result = "["; for (int i = 0; i < arr.length; i++) { result += (i == arr.length-1 ? arr[i]: arr[i] + ", "); } result += "]"; return result; } public static void main(String[] args) { int[] array1 = {1, 2, 3, 4}; System.out.println(ArrayUtil.toSting(array1)); int[] array2 = {}; System.out.println(ArrayUtil.toSting(array2)); int[] array3 = null; System.out.println(ArrayUtil.toSting(array3)); } }
7:static 代码块 初始化数据
代码块概述 代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。 在Java类下,使用 { } 括起来的代码被称为代码块 。 代码块分为 静态代码块: 属于类 格式:static{} 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次 使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用 构造代码块(了解,见的少): 格式:{} 特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行 使用场景:初始化实例资源
package com.itheima.d3_static_code; public class StaticDemo1 { public static String schoolName; /** 静态代码块:有static修饰,属于类,与类一起优先加载一次,自动触发执行。 作用:可以用于初始化静态资源。 跟类一起加载,执行在 mian 方法之前 */ static { System.out.println("------静态代码块被触发执行了------"); schoolName = "黑马"; } public static void main(String[] args) { // 目标:先理解静态代码块。 System.out.println("------main方法执行------"); System.out.println(schoolName); } }
package com.itheima.d3_static_code; public class StaticDemo2 { private String name; public StaticDemo2(){ System.out.println("===无参构造器被触发执行=="); } /** 实例代码块【构造代码块】:无static修饰,属于对象,每次构建对象时,都会触发一次执行。 初始化实例资源。 */ { //name = "张三"; System.out.println("===实例代码块被触发执行==="); } public static void main(String[] args) { // 目标:理解实例代码块(构造代码块)。 StaticDemo2 s1 = new StaticDemo2(); System.out.println(s1.name); StaticDemo2 s2 = new StaticDemo2(); System.out.println(s2.name); } }
在启动游戏房间的时候,应该提前准备好54张牌,后续才可以直接使用这些牌数据
该房间只需要一副牌。
定义一个静态的ArrayList集合存储54张牌对象,静态的集合只会加载一份。
在启动游戏房间前,应该将54张牌初始化好
当系统启动的同时需要准备好54张牌数据,此时可以用静态代码块完成
package com.itheima.d3_static_code; import java.util.ArrayList; public class StaticTest3 { /** 1、定义一个静态的集合,这样这个集合只加载 一个。因为当前房间只需要一副牌。 */ public static ArrayList<String> cards = new ArrayList<>(); /** 2、在程序真正运行main方法前,把54张牌放进去吧,后续游戏可以直接使用了。 */ static { // 3、正式做牌,放到集合中去。 // a、定义一个数组存储全部点数:类型确定了,个数确定了。 String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"}; // b、定义一个数组存储全部的花色:类型确定了,个数确定了。 String[] colors = {"♥", "♠", "♦", "♣"}; // c、遍历点数 for (int i = 0; i < sizes.length; i++) { // sizes[i] // d、遍历花色 for (int j = 0; j < colors.length; j++) { // colors[j] // 一张牌 String card = sizes[i] + colors[j]; cards.add(card); } } // e、单独加入大小王。 cards.add("小🃏"); cards.add("大🃏"); } public static void main(String[] args) { // 目标:模拟游戏启动前,初始化54张牌数据。 System.out.println("新牌:" + cards); } }
8:static 单例设计模式
什么是设计模式(Design pattern)
开发中经常遇到一些问题,一个问题通常有n种解法的,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
设计模式有20多种,对应20多种软件开发中会遇到的问题。
学设计模式主要是学2点:
第一:这种模式用来解决什么问题。
第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的
单例模式
可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间
单例的实现方式很多
饿汉单例模式。
懒汉单例模式。
饿汉单例设计模式
在用类获取对象的时候,对象已经提前为你创建好了
设计步骤:
定义一个类,把构造器私有。
定义一个静态变量存储一个对象
public class SingleInstance /** c.定义一个静态变量存储一个对象即可 :属于类,与类一起加载一次 * public static SingleInstance instance = new SingleInstance (); /** b.单例必须私有构造器* private SingleInstance () System.out.println("创建了一个对象") } }
package com.itheima.d4_static_singleinstance; /** 使用饿汉单例实现单例类 */ public class SingleInstance { /** 2、饿汉单例是在获取对象前,对象已经提前准备好了一个。 这个对象只能是一个,所以定义静态成员变量记住。 */ public static SingleInstance instance = new SingleInstance(); /** 1、必须把构造器私有化。 */ private SingleInstance(){ } public static void main(String[] args) { SingleInstance s1 = SingleInstance.instance; SingleInstance s2 = SingleInstance.instance; System.out.println(s1 == s2); } }
懒汉单例设计模式
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
定义一个类,把构造器私有。
定义一个静态变量存储一个对象。
提供一个返回单例对象的方法
package com.itheima.d4_static_singleinstance; /** 懒汉单例 */ public class SingleInstance2 { /** 2、定义一个静态的成员变量负责存储一个对象。 只加载一次,只有一份。 注意:最好私有化,这样可以避免给别人挖坑! */ private static SingleInstance2 instance; /** 3、提供一个方法,对外返回单例对象。 */ public static SingleInstance2 getInstance() { if(instance == null){ // 第一次来拿对象 :此时需要创建对象。 instance = new SingleInstance2(); } return instance; } /** 1、私有化构造器 */ private SingleInstance2(){ } public static void main(String[] args) { // 目标: 掌握懒汉单例的设计。理解其思想。 SingleInstance2 s1 = SingleInstance2.getInstance(); SingleInstance2 s2 = SingleInstance2.getInstance(); System.out.println(s1 == s2); } }
9:继承 extends 关键字
什么是继承? Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。 public class Student extends People {} Student称为子类(派生类),People称为父类(基类 或超类)。 作用:当子类继承父类后,就可以直接使用父类公共的属性和方法了 使用继承的好处 可以提高代码的复用性
10:继承的 内存运行原理
继承设计规范:
子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的的属性和行为应该定义在子类自己里面
为什么?
如果子类的独有属性、行为定义在父类中,会导致其它子类也会得到这些属性和行为,这不符合面向对象逻辑。
如下:对外一个子类对象,但是分成两个空间,子类空间和父类空间【对外一个对象】
11:继承的特点
继承的特点
子类可以继承父类的属性和行为,但是 子类不能继承父类的构造器。
Java是单继承模式:一个类只能继承一个直接父类。
Java不支持多继承、但是支持多层继承。【多个父类有多个相同方法,调用的使用不知道使用那个父类的方法,跟python不同】
Java中所有的类都是Object类的子类。
子类是否可以继承父类的构造器?
不可以的,子类有自己的构造器,父类构造器用于初始化父类对象
子类是否可以继承父类的私有成员?
可以的,只是不能直接访问
子类是否可以继承父类的静态成员
子类可以直接使用父类的静态成员(共享)
个人认为:子类不能继承父类的静态成员(共享并非继承)
package com.itheima.d7_extends_feature; public class Test { public static void main(String[] args) { // 目标:理解继承的特点。 // 1、子类不能继承父类的构造器 // 2、子类是否可以继承父类的私有成员? 我认为是可以继承父类私有成员的,只是不能直接访问。 Tiger t = new Tiger(); // t.eat(); // 3、子类是否可以继承父类的静态成员。 我认识不算继承的。只是共享的。 System.out.println(Tiger.location); } }
class Animal{ private void eat(){ System.out.println("动物要吃东西~~"); } public static String location = "长隆动物园"; }
class Tiger extends Animal{ }
12:继承之 成员变量,成员方法的访问
在子类方法中访问成员(成员变量、成员方法)满足:就近原则 先子类局部范围找 然后子类成员范围找 然后父类成员范围找,如果父类范围还没有找到则报错 如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办? 可以通过super关键字,指定访问父类的成员。 格式:super.父类成员变量/父类成员方法
this super 的使用
package com.itheima.d8_extends_field_method; public class Test { public static void main(String[] args) { // 目标:理解继承后成员的访问特点:就近原则。 Dog d = new Dog(); d.run(); // 子类的 d.lookDoor(); // 子类的 d.showName(); } } class Animal{ public String name = "动物名"; public void run(){ System.out.println("动物可以跑~~"); } } class Dog extends Animal{ public String name = "狗名"; public void lookDoor(){ System.out.println("狗可以看门~~"); } public void showName(){ String name = "局部名"; System.out.println(name); System.out.println(this.name); // 当前子类对象的name System.out.println(super.name); // 找父类的name super.run(); // 找父类的方法 run(); // 子类的run } public void run(){ System.out.println("狗跑的贼快~~~"); } }
13:继承之 方法重写 @overdide
什么是方法重写?
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。
方法重写的应用场景
当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
子类可以重写父类中的方法。
案例演示:
旧手机的功能只能是基本的打电话,发信息
新手机的功能需要能够:基本的打电话下支持视频通话。基本的发信息下支持发送语音和图片
@Override重写注解 @Override是放在重写后的方法上,作为重写是否正确的校验注解。 加上该注解后如果重写错误,编译阶段会出现错误提示。 建议重写方法都加@Override注解,代码安全,优雅! 方法重写注意事项和要求 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。 私有方法不能被重写。 父类 privite 修饰的 子类重写父类方法时,访问权限必须大于或者等于父类 (暂时了解 :缺省 < protected < public) 【一般访问权限和父类保持一致】
私有权限:最小privite【本类】 缺省:在本类和本包下面访问 protected:本类,本包,其他类,其他包的子类中可以访问 public: 子类不能重写父类的静态方法,如果重写会报错的。【静态方法属于父类本身,父类的静态方法父类的类名调用,子类的静态方法子类的类名调用】
package com.itheima.d9_extends_override; public class Test { public static void main(String[] args) { // 目标:认识方法重写。 NewPhone hw = new NewPhone(); hw.call(); hw.sendMsg(); } } /** 新手机:子类。 */ class NewPhone extends Phone{ // 重写的方法 // 1、@Override重写校验注解,加上之后,这个方法必须是正确重写的,这样更安全。2、提高程序的可读性,代码优雅! // 注意:重写方法的名称和形参列表必须与被重写的方法一模一样。 @Override public void call(){ super.call(); // 先用它爸爸的基本功能 System.out.println("开始视频通话~~"); } // 重写的方法 @Override public void sendMsg(){ super.sendMsg(); // 先用它爸爸的基本功能 System.out.println("发送有趣的图片~"); } // 注意:静态方法不能被重写。 // @Override // public static void test(){ // // } } /** 旧手机:父类的 */ class Phone{ public void call(){ System.out.println("打电话~"); } public void sendMsg(){ System.out.println("发短信~"); } public static void test(){ } }
13:继承之 子类构造器特点
子类继承父类后构造器的特点: 子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。 为什么? 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。 子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。 怎么调用父类构造器的? 子类构造器的第一行语句默认都是:super(),不写也存在
package com.itheima.d10_extends_constructor; public class Test { public static void main(String[] args) { // 目标:认识继承后子类构造器的特点 // 特点:子类的全部构造器默认会先访问父类的无参数构造器再执行自己 Dog d1 = new Dog(); System.out.println(d1); System.out.println("-----"); Dog d2 = new Dog("金毛"); System.out.println(d2); } } class Dog extends Animal{ public Dog(){ // super(); // 写不写都有,默认找父类的无参数构造器执行 System.out.println("子类Dog无参数构造器被执行~"); } public Dog(String name){ // super(); // 写不写都有,默认找父类的无参数构造器执行 System.out.println("子类Dog有参数构造器被执行~"); } } class Animal { public Animal(){ System.out.println("父类Animal无参数构造器被执行~"); } }
14:继承之 子类构造器访问父类的有参构造器
super调用父类有参数构造器的作用: 初始化继承自父类的数据。 如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢? 会报错。因为子类默认是调用父类无参构造器的。 如何解决? 子类构造器中可以通过书写 super(…),手动调用父类的有参数构造器
super调用父类有参数构造器的作用
初始化继承自父类的数据【通过调用父类的有参数构造器来初始化继承来自父类的数据】
如果父类中没有无参数构造器,只有有参数构造器,会出现什么情况
报错,因为子类中默认的构造器都会调用父类的无参数构造器
子类可以通过 super() 手动调用父类的各种构造器,有参,无参的都可以 【父类的有参,无参构造器都要提供】
package com.itheima.d11_extends_constructor; public class Test { public static void main(String[] args) { // 目标:学习子类构造器如何去访问父类有参数构造器,还要清楚其作用。 Teacher t = new Teacher("dlei", 18); System.out.println(t.getName()); System.out.println(t.getAge()); } } class People { private String name; private int age; public People(){ } public People(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; } } class Teacher extends People{ public Teacher(){ } public Teacher(String name, int age){ super(name, age); // 调用父类的有参数构造器:初始化继承自父类的数据 // 不能使用 this.name 赋值,因为name不在子类空间,在父类空间,需要使用super // super.setName(name); // 这样也能初始化数据 // super.setAge(age); } }
15:this + super 关键字使用
package com.itheima.d12_this; public class Test { public static void main(String[] args) { // 目标:理解this(...)的作用:本类构造器中访问本类兄弟构造器。 Student s1 = new Student("殷素素", "冰火岛自学"); System.out.println(s1.getName()); System.out.println(s1.getSchoolName()); /** 如果学生不填写学校,默认这个对象的学校是黑马 */ Student s2 = new Student("张三丰"); System.out.println(s2.getName()); System.out.println(s2.getSchoolName()); } } class Student { private String name; private String schoolName; public Student() { } public Student(String name) { // 如果学生不填写学校,默认这个对象的学校是黑马 [只接名称] this(name, "灰度程序员"); // 借用本类兄弟构造器 } public Student(String name, String schoolName) { // super(); // 必须先初始化父类,再初始化自己。 this.name = name; this.schoolName = schoolName; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSchoolName() { return schoolName; } public void setSchoolName(String schoolName) { this.schoolName = schoolName; } }
this(...)和super(…)使用注意点: 子类通过 this (...)去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。 注意:this(…) super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。
16:包 和 导包 包的声明
什么是包? 包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护。 建包的语法格式:package 公司域名倒写.技术名称。报名建议全部英文小写,且具备意义 package com.itheima.javabean; public class Student { } 建包语句必须在第一行,一般IDEA工具会帮助创建 导包 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式:import 包名.类名; 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
alt + 回车 快捷导包
idea 可以配置自动导包
工程结构
package com.itheima.d1_package.it; public class Student { }
com.itheima.di_package.it2.Student.class
package com.itheima.d1_package.it2; public class Student { }
com.itheima.di_package.User.class
package com.itheima.d1_package; public class User { public static int onlineNumber = 121; }
com.itheima.di_package.Test.class
package com.itheima.d1_package; // 导包。 import com.itheima.d1_package.it.Student; import java.util.Scanner; public class Test { public static void main(String[] args) { // 目标:理解以下两点 // 1、同一个包下的类,互相可以直接访问。 System.out.println(User.onlineNumber); // 2、不同包下的类,必须先导包才可以访问。 Student s = new Student(); Scanner sc = new Scanner(System.in); // 3、如果这个类中使用不同包下的相同的类名, // 此时默认只能导入一个类的包,另一个类要使用全名访问。 com.itheima.d1_package.it2.Student s2 = new com.itheima.d1_package.it2.Student(); } }
17:权限修饰符 private【私有】 -> 缺省【默认,包访问权限】 -> protected【子类访问权限】 - > public 【】
什么是权限修饰符? 权限修饰符:是用来控制一个成员能够被访问的范围。 可以修饰 成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制 权限修饰符的分类和具体作用范围: 权限修饰符:有四种作用范围由小到大(private -> 缺省 -> protected - > public )
工程目录
package com.itheima.d2_modifier.itcast; import com.itheima.d2_modifier.Fu; public class Tes2 { public static void main(String[] args) { // 其他包下的无关类 中 只能 访问 public Fu f = new Fu(); //f.privateMethod(); // 报错 //f.method();// 报错 //f.protectedMethod();// 报错 f.publicMethod(); } }
com.itheima.d2_modifier.itcast.Zi.class
package com.itheima.d2_modifier.itcast; import com.itheima.d2_modifier.Fu; public class Zi extends Fu { public static void main(String[] args) { // 其他包下的子类,只能访问 pubulic 和 protected 【子类对象才可以访问,父类对象不能访问】 Zi zi = new Zi(); zi.protectedMethod(); zi.publicMethod(); Fu f = new Fu(); f.publicMethod(); } }
com.itheima.d2_modifier.Fu.class
package com.itheima.d2_modifier; public class Fu { /** 1、定义私有的成员: private 只能本类中访问。 */ private void privateMethod(){ System.out.println("---private--"); } /** 2、定义缺省修饰的成员: 只能本类中、同包下其他类访问(包访问权限) */ void method(){ System.out.println("---缺省--"); } /** 3、protected修饰的方法:本类,同包的其他类中,其他包的子类中。 */ protected void protectedMethod(){ System.out.println("---protected--"); } /** 4、public修饰的方法:本类,同包的其他类中,其他包的子类中,其他包的无关类中。 */ public void publicMethod(){ System.out.println("---public--"); } public static void main(String[] args) { // 本类中的,4种权限都能访问 Fu f = new Fu(); f.privateMethod(); f.method(); f.protectedMethod(); f.publicMethod(); } }
com.itheima.d2_modifier.Test.class
package com.itheima.d2_modifier; public class Test { public static void main(String[] args) { // 目标:讲解权限修饰符的修饰范围。明白每一种修饰后的作用范围。 Fu f = new Fu(); // f.privateMethod(); // 报错的,私有的 同包下的,私有不能访问 f.method(); f.protectedMethod(); f.publicMethod(); } }
能够识别别人定义的成员的访问范围。
自己定义成员(方法,成员变量,构造器等)一般需要满足如下要求:
成员变量一般私有。 privite
方法一般公开。 public
如果该成员只希望本类访问,使用private修饰。
如果该成员只希望本类,同一个包下的其他类和子类访问,使用protected修饰。
18:final 修饰符
final的作用 final 关键字是最终的意思,可以修饰(类、方法、变量) 修饰类:表明该类是最终类,不能被继承。 修饰方法:表明该方法是最终方法,不能被重写。 修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次) final修饰变量的注意 final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。 final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的
package com.itheima.d3_final; public class Test { public static void main(String[] args) { // 目标:记住final的语法。 // 1、final修饰类,类不能被继承。 // 2、final修饰方法,方法不能被重写 // 3、final修饰变量,总规则:变量有且仅能被赋值一次。 } } class Student extends People{ // @Override // public void eat() { // 2、final修饰方法,方法不能被重写 // System.out.println("学生吃的很多~~"); // } } class People{ public final void eat(){ System.out.println("人都要吃东西~~"); } } //class Wolf extends Animal{ // 1、final修饰类,类不能被继承。[工具类使用 final 修饰] //} //final class Animal{ //}
package com.itheima.d3_final; public class Test2 { /** 二:修饰静态成员变量(public static final修饰的也称为常量了),final定义的时候就要赋值,不然报错 */ public static final String schoolName = "黑马"; /** 三,修饰实例成员变量。(几乎不用) final定义的时候就要赋值,不然报错 */ private final String name = "猪刚鬣"; public static void main(String[] args) { // 目标:理解final修饰变量的作用:总规则:变量有且仅能被赋值一次。(理解语法) // 变量有几种: // 1、局部变量 // 2、成员变量 // -- 实例成员变量。 // -- 静态成员变量。 // 一:修饰局部变量【修饰局部变量的一种】 final double rate = 3.14; // rate = 3.19; // 第二次赋值了会报错。 buy(0.8); // schoolName = "黑马程序员"; // 第二次赋值了。 Test2 t = new Test2(); System.out.println(t.name); // t.name = "天蓬元帅"; // 注意:final修饰引用类型的变量,其地址值不能改变,但是指向的对象的内容可以改变的。 final Teacher t2 = new Teacher("学习,授课,吹吹水~~"); // t2 = null; // 第二次赋值了。 System.out.println(t2.getHobby()); t2.setHobby("运动"); System.out.println(t2.getHobby()); } public static void buy(final double z){ // 二:修饰传参【修饰局部变量的一种】 // z = 0.1; // z不能第二次赋值了。,接收的时候就已经赋值一次了 } } class Teacher{ private String hobby; public Teacher(String hobby) { this.hobby = hobby; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } }
19:常量
常量 常量是使用了 public static final 修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变。 常量名的命名规范:英文单词全部大写,多个单词下划线连接起来。 常量的作用:通常用来记录系统的配置数据。 public class Constant { public static final String SCHOOL_NAME = “传智教育"; public static final String LOGIN_NAME = “admin"; public static final String PASS_WORD = “123456"; } 常量做信息配置的原理、优势 在编译阶段会进行“宏替换”:把使用常量的地方全部替换成真实的字面量。 维护系统容易,可读性更好
案例说明:
现在开发的超级玛丽游戏需要接收用户输入的四个方向的信号(上下左右),以便控制玛丽移动的方向。
选择常量做信息标志和分类:【如下案例】
代码可读性好,实现了软编码形式
public class Constant { // 常量 public static final int UP = 1; public static final int DOWN = 2; public static final int LEFT = 3; public static final int RIGHT = 4; }
package com.itheima.d4_constant; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Test2 { public static void main(String[] args) { // 目标:理解常量的另外一个作用:做信息的标志和分类。 // 模拟:控制玛丽的移动:上下左右。 // 桌面编程 // 1、创建窗口 JFrame win = new JFrame("超级玛丽"); win.setSize(300, 200); // 2、设计一个面板(桌布) JPanel jPanel = new JPanel(); win.add(jPanel); // 2、放置几个按钮 JButton b1 = new JButton("上"); jPanel.add(b1); b1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往上跳 move(Constant.UP); } }); JButton b2 = new JButton("下"); jPanel.add(b2); b2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往下蹲 move(Constant.DOWN); } }); JButton b3 = new JButton("左"); jPanel.add(b3); b3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往左走 move(Constant.LEFT); } }); JButton b4 = new JButton("右"); jPanel.add(b4); b4.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往右走 move(Constant.RIGHT); } }); win.setLocationRelativeTo(null); win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); win.setVisible(true); } public static void move(int orientation){ // 控制玛丽移动 switch (orientation) { case Constant.UP: System.out.println("玛丽往↑飞了一下~~"); break; case Constant.DOWN: System.out.println("玛丽往↓蹲了一下~~"); break; case Constant.LEFT: System.out.println("玛丽往←跑了一下~~"); break; case Constant.RIGHT: System.out.println("玛丽往→跑了一下~~"); break; } } }
20:枚举 【信息标志和分类 使用 枚举 和 常量 登录用户名和信息这些还是使用常量】
枚举是Java中的一种特殊类型【特殊类】 枚举的作用:"是为了做信息的标志和信息的分类"。 定义枚举类的格式: 修饰符 enum 枚举名称{ 第一行都是罗列枚举类实例的名称。 } enum Season{
SPRING , SUMMER , AUTUMN , WINTER;}
enum Season{
SPRING , SUMMER , AUTUMN , WINTER;
}
// 当前枚举类的对象名称,4个季节对象,多例模式
反编译后观察枚举的特征: Compiled from "Season.java" public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING = new Season();
public static final Season SUMMER = new Season();
public static final Season AUTUMN = new Season();
public static final Season WINTER = new Season();
public static Season[] values();
public static Season valueOf(java.lang.String);
}
枚举的特征:
枚举类都是继承了枚举类型:java.lang.Enum
枚举都是 final 最终类,不可以被继承。
构造器都是私有的,枚举对外不能创建对象。
枚举类的第一行默认都是罗列枚举对象的名称的。【每个名称都成存储的一个final 常量,而且是当前类的对象】
枚举类相当于是多例模式 【对外不能创建对象,里面罗列出几个对象,几个变量创建几个对象】
package com.itheima.d5_enum; public enum Constant { UP,DOWN,LEFT,RIGHT; }
package com.itheima.d5_enum; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Test2 { public static void main(String[] args) { // 目标:使用枚举做信息标志和分类(最好的方案) // 1、创建窗口 JFrame win = new JFrame("超级玛丽"); win.setSize(300, 200); // 2、设计一个面板(桌布) JPanel jPanel = new JPanel(); win.add(jPanel); // 2、放置几个按钮 JButton b1 = new JButton("上"); jPanel.add(b1); b1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往上跳 move(Constant.UP); } }); JButton b2 = new JButton("下"); jPanel.add(b2); b2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往下蹲 move(Constant.DOWN); } }); JButton b3 = new JButton("左"); jPanel.add(b3); b3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往左走 move(Constant.LEFT); } }); JButton b4 = new JButton("右"); jPanel.add(b4); b4.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 控制玛丽往右走 move(Constant.RIGHT); } }); win.setLocationRelativeTo(null); win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); win.setVisible(true); } public static void move(Constant orientation){ // 控制玛丽移动 switch (orientation) { // switch兼容枚举类型,不需要 类名. 找对象,直接就能找到对象 case UP: System.out.println("玛丽往↑飞了一下~~"); break; case DOWN: System.out.println("玛丽往↓蹲了一下~~"); break; case LEFT: System.out.println("玛丽往←跑了一下~~"); break; case RIGHT: System.out.println("玛丽往→跑了一下~~"); break; } } }
21:抽象类 abstract 【规范子类的行为】【抽象:设计图,不能实例化】
在Java中abstract是抽象的意思,可以修饰类、成员方法。 abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。 修饰符 abstract class 类名{ 修饰符 abstract 返回值类型 方法名称(形参列表); } public abstract class Animal{ public abstract void run(); } 注意事项 抽象方法只有方法签名,不能声明方法体。 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错
final 和 abstract:
互斥关系
final 修饰类不能被继承,abstract修饰类一般是要被继承的
final修饰的方法一般不能被重写,abstract修饰的方法一般是要被重写的
抽象的使用场景: 抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承。 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类
抽象类注意点:
一个类继承了抽象类,那么这个类需要完成父类抽象类中的全部抽象方法,否则这个类也需要被定义成抽象类 public abstract class Animal public abstract void run(); }
如下案例:
抽象类【父类】Animal 要求 子类 Dog 一定重写 run 这个实例方法【不重写 run 方法报错】
package com.itheima.d6_abstract; /** 抽象类:有abstract修饰 */ public abstract class Animal { private String name; /** 抽象方法:有abstract修饰 不能写方法体代码 */ public abstract void run(); public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.itheima.d6_abstract; public class Dog extends Animal{ @Override public void run() { System.out.println("狗跑的贼快~~"); } }
package com.itheima.d7_abstract_test; public abstract class Card { private String userName; private double money; /** 定义一个支付方法:表示卡片可以支付。 抽象方法 */ public abstract void pay(double money2); public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } }
package com.itheima.d7_abstract_test; public class GoldCard extends Card{ @Override public void pay(double money2) { System.out.println("您当前消费:" + money2); System.out.println("您卡片当前余额是:" + getMoney()); // 优惠价: double rs = money2 * 0.8; System.out.println(getUserName() + ":您实际支付:" + rs); // 更新账户余额 setMoney(getMoney() - rs); } }
package com.itheima.d7_abstract_test; public class Test { public static void main(String[] args) { // 目标:学习一下抽象类的基本使用:做父类,被继承,重写抽象方法 GoldCard c = new GoldCard(); c.setMoney(10000); c.setUserName("dlei"); c.pay(300); System.out.println("剩余:" + c.getMoney()); } }
抽象类特征和注意事项
类有的成员(成员变量、方法、构造器)抽象类都具备
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
不能用abstract 修饰 变量、代码块、构造器 【只能修饰类和方法】
最重要的特征:
得到了抽象方法,失去了创建对象的能力(有得有失)【抽象类不能创建对象】
package com.itheima.d8_abstract_attention; public class Test { public static void main(String[] args) { // 目标:理解抽象类的特征和注意事项。 // 1、类有的东西,抽象类都有。 // 2、抽象类中可以没有抽象方法,但是有抽象方法的必须是抽象类。 // 3、一个类继承了抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。 // 4、抽象类不能创建对象 ,为什么?? // 反证法:假如抽象类可以创建对象。 // Animal a = new Animal(); // a.run(); // run方法连方法体都没有! 因此抽象类不能创建对象。 // Card c = new Card(); // 不行的,抽象类不能创建对象(这个观点不能动摇)。 } } abstract class Card{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } abstract class Cat extends Animal{ @Override public void run() { } } abstract class Animal{ public abstract void run(); public abstract void eat(); }
22:模板设计方式 之 模板方法设计模式【抽象类 的实际使用--模板方法模式】
什么时候使用模板方法模式: 使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。 模板方法模式实现步骤: 1、定义一个抽象类。【模板功能定义成一个模板方法,放在抽象类中】
2、定义2个方法,一个是模板方法:把相同代码放里面去,不同代码定义成抽象方法 3、子类继承抽象类,重写抽象方法。【模板方法不能决定的功能定义成抽象方法让具体子类去实现】
模拟方法建议使用 final 修饰:
模板方法是给子类使用的,不是让子类重写的,一旦子类重写了模板方法,那么模板就失效了,加上 final 后可以防止子类重写模板方法。专业安全
package com.itheima.d9_abstract_template; public abstract class Student { /** 正式:声明了模板方法模式 final :这个方法不能被子类重写,因为它是给子类直接使用的。 */ public final void write(){ System.out.println("\t\t\t\t《我的爸爸》"); System.out.println("你的爸爸是啥样,来说说:"); // 正文部分(每个子类都要写的,每个子类写的情况不一样 [因此。模板方法把正文部分定义成抽象方法,交给具体的子类来完成)] System.out.println(writeMain()); // 子类调用这个方法的时候会调子类自己重写的方法 System.out.println("我的爸爸简直太好了~~"); } public abstract String writeMain(); }
package com.itheima.d9_abstract_template; public class StudentChild extends Student{ @Override public String writeMain() { return "的爸爸太牛b了,他总是买东西给我吃。。"; } }
package com.itheima.d9_abstract_template; public class StudentMiddle extends Student{ @Override public String writeMain() { return "我的爸爸也很牛,我开车都不看红绿灯的," + "下辈子还要做他儿子~~"; } }
package com.itheima.d9_abstract_template; public class Test { public static void main(String[] args) { // 目标:理解模板方法模式的思想和使用步骤。 StudentMiddle s = new StudentMiddle(); s.write(); StudentChild s2 = new StudentChild(); s2.write(); } }
23:面向对象之 接口 interface【也是一种代码规范,约定人家只能干啥,比抽象类约束性更强】
接口的定义与特点 接口的格式如下: 接口用关键字interface来定义 public interface 接口名 { // 常量 static final 修饰的变量 // 抽象方法 abstract修饰的方法 } JDK8之前接口中只能是抽象方法和常量,没有其他成分了。 接口不能实例化。 接口中的成员都是public修饰的,写不写都是,因为规范的目的是为了公开化。
package com.itheima.d10_interface; /** 声明了一个接口: 体现一种规范,规范一定是公开的。 */ public interface InterfaceDemo { // 目标:接口中的成分特点:JDK 8之前接口中只能有抽象方法和常量。 // 1、常量: // 注意:由于接口体现规范思想,规范默认都是公开的,所以代码层面,public static final 可以省略不写【自动加】 String SHCOOL_NAME = "天才程序员"; //public static final String SHCOOL_NAME = "天才程序员"; // 2、抽象方法 // 注意:由于接口体现规范思想,规范默认都是公开的,所以代码层面,public abstract可以省略不写【自动加】 void run(); // public abstract void run(); void eat(); // public abstract void eat(); }
24:接口的基本使用 被类实现 implements
接口的用法: 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类 修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... { }
实现的关键字:implements 上面可以看出,接口可以被类单实现,也可以被类多实现。 接口实现的注意事项: 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类
package com.itheima.d11_interface_implements; /** 规范 */ public interface SportMan { void run(); // 运动员一点要跑步 void competition(); // 运动员一点要比赛 }
package com.itheima.d11_interface_implements; public interface Law { void rule();// 遵纪守法 }
package com.itheima.d11_interface_implements; /** 实现类。 */ public class PingPongMan implements SportMan, Law{ private String name; public PingPongMan(String name) { this.name = name; } @Override public void run() { System.out.println(name + "必须跑步训练!"); } @Override public void competition() { System.out.println(name + "要参加比赛,为国争光~!"); } @Override public void rule() { System.out.println(name + "必须守法~~"); } }
package com.itheima.d11_interface_implements; public class Test { public static void main(String[] args) { // 目标:理解接口的基本使用:被类实现。 PingPongMan p = new PingPongMan("张继科"); p.run(); p.competition(); p.rule(); } }
25:接口与接口的关系 【接口支持多继承】
类和类的关系:单继承。
类和接口的关系:多实现。
接口和接口的关系:多继承,一个接口可以同时继承多个接口
接口多继承的作用
规范合并,整合多个接口为同一个接口,便于子类实现。
package com.itheima.d12_interface_extends; public interface Law { void rule(); }
package com.itheima.d12_interface_extends; public interface People { void eat(); void sleep(); }
package com.itheima.d12_interface_extends; /** 接口可以多继承:一个接口可以同时继承多个接口。 */ public interface SportMan extends Law, People { void run(); void competition(); }
package com.itheima.d12_interface_extends; /** 一个实现类的: */ public class BasketBallMan implements SportMan{ @Override public void rule() { } @Override public void eat() { } @Override public void sleep() { } @Override public void run() { } @Override public void competition() { } }
26:JDK8新增接口相关方法 允许接口中直接定义带有方法体的方法【接口不再存粹】
第一种:默认方法 【普通实例方法 使用 default 修饰】 类似之前写的普通实例方法:必须用default修饰 默认会public修饰。需要用接口的实现类的对象来调用 default void run(){ System.out.println("--开始跑--"); } 第二种:静态方法 默认会public修饰,必须static修饰。 注意:接口的静态方法必须用本身的接口名来调用 【实现类名字 无法调用】 static void inAddr(){ System.out.println("我们都在黑马培训中心快乐的学习Java!"); } 第三种:私有方法 【本接口被其他默认方法和私有方法访问】 就是私有的实例方法:,必须使用private修饰,从JDK 1.9才开始有的。 只能在本类中被其他的默认方法或者私有方法访问 private void go(){ System.out.println("--准备--"); }
package com.itheima.d13_interface_jdk8; public interface SportMan { /** 1、默认方法:其实就是实例方法。【接口不能创建对象,这个方法只能过继给实现类,由实现类对象调用】 -- 必须用default修饰,默认会自带public -- 必须用接口的实现类的对象来调用 */ default void run(){ go(); System.out.println("==跑的贼溜=="); } /** 2、静态方法 -- 必须static修饰,默认会自带public -- 必须用接口名自己调用 */ static void inAddr(){ System.out.println("我们在黑马"); } /** 3、私有实例方法: -- 必须用private修饰 -- 只能本接口中访问。 -- 一般给接口中其他的默认方法或者私有实例方法使用的 */ private void go(){ System.out.println("开始跑~~"); } }
package com.itheima.d13_interface_jdk8; // 实现类 public class PingPongMan implements SportMan{ }
package com.itheima.d13_interface_jdk8; public class Test { public static void main(String[] args) { // 目标:理解JDK 8开始接口新增的方法。 PingPongMan p = new PingPongMan(); p.run(); SportMan.inAddr(); } }
接口的注意事项 1、接口不能创建对象【接口构造器,更加彻底的抽象】 2、一个类实现多个接口,多个接口的规范不能冲突 2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。【静态方法只能 接口的名称去调用,所以不会冲突,接口里子类调用不了父类的静态方法】 3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。【优先使用父类的,毕竟亲爹】 4、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可【子类使用不了 接口 干爹的方法】 5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承 【两个规范方法,一个方法有返回值,一个方法没有返回值这种,就不行,规范不能冲突】
package com.itheima.d14_interface_attention; public class Test { public static void main(String[] args) { // 目标:了解接口使用的注意事项。 // 1、接口不能创建对象 // A a = new A(); // 2、一个类实现多个接口,多个接口的规范不能冲突 // 3、一个类实现多个接口,多个接口中有同样的静态方法不冲突。(接口的静态方法只能接口自己调) A.test(); B.test(); // 4、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。 Zi zi = new Zi(); zi.go(); // 5、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。 Zi2 zi2 = new Zi2(); zi2.sing(); // 6、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。 } } // 6、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。 //interface K extends I, J{ // //} //interface I{ // void run(); //} // //interface J{ // String run(); //} // 5、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。 class Zi2 implements GanDie1, GanDie2{ @Override public void sing() { GanDie1.super.sing(); GanDie2.super.sing(); System.out.println("我唱给大家听了"); } } interface GanDie1{ default void sing(){ System.out.println("干爹1让你唱小苹果~"); } } interface GanDie2{ default void sing(){ System.out.println("干爹2让你唱征服~"); } } // 4、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。 class Zi extends Fu implements GanDie{ public void go(){ GanDie.super.run(); // 找干爹这个爸爸的run方法。 } } class Fu { public void run(){ System.out.println("亲爹说赶紧跑路~~"); } } interface GanDie{ default void run(){ System.out.println("干爹说留在我身边~~"); } } /** 接口是干爹。 */ class C implements A, B{ @Override public void run() { } } interface B{ void run(); static void test(){ } } interface A{ void run(); static void test(){ } }
27:面向对象之 多态 父类类型 对象名称 = new 子类构造器; 【有继承关系才有多态】
什么是多态? 指对象可以有多种形态。【子类覆盖父类的方法,定义很多同类型的对象,执行同一个行为,会出现不同的行为特征】 多态的常见形式: 父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器; 多态中成员访问特点 方法调用:编译看左边,运行看右边。 变量调用:编译看左边,运行也看左边。(注意) 多态的前提 有继承/实现关系;有父类引用指向子类对象;有方法重写(多态侧重行为多态)。
package com.itheima.d1_polymorphic; class Animal { public String name = "动物名称"; public void run(){ System.out.println("动物可以跑~~"); } } class Dog extends Animal{ public String name = "狗名称"; @Override public void run() { System.out.println("🐕跑的贼溜~~~~~"); } } class Tortoise extends Animal{ public String name = "乌龟名称"; @Override public void run() { System.out.println("🐢跑的非常慢~~~"); } } public class Test001 { public static void main(String[] args) { // 目标:先认识多态的形式 // 父类 对象名称 = new 子类构造器(); 狗也是属于动物类型,动物范围大,小类型变量给一个大类型,可以这样操作
// 【同类型变量 a和 a1都是Animal类型,调用同一行为,但是行为特征不同---多态】 Animal a = new Dog(); a.run(); // 方法调用:编译看左,运行看右 所以Animal里还是要定义这个 run 方法,不然编译报错的,子类里还要进行方法重写 System.out.println(a.name); // 变量调用:编译看左,运行也看左,动物名称 所以访问的还是Animal这个对象里定义的name, Animal a1 = new Tortoise(); a1.run(); System.out.println(a1.name); // 动物名称 } }
28:面向对象之 多态的优势
优势
在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
Animal a = new Dog();
a.run(); // 后续业务行为随对象而变,后续代码无需修改 【可以使用 Dog 也可以使用 Tortoise】
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
【父类类型对象可以接收一切子对象】
多态下会产生的一个问题:
多态下不能使用子类的独有功能【使用强制类型转换解决】
public static void go(Animal a){ }
这个方法可以接收一切 Animal和他子类对象,可以是Dog对象也可以是 Tortoise 对象,
方法的扩展性更强也更更便利 【多态使用的案例】
多态下不能使用子类的独有功能:
package com.itheima.d2_polymorphic_advantage; /** 父类 */ public class Animal { public String name = "动物名称"; public void run(){ System.out.println("动物可以跑~~"); } }
package com.itheima.d2_polymorphic_advantage; public class Dog extends Animal { public String name = "狗名称"; @Override public void run() { System.out.println("🐕跑的贼溜~~~~~"); } /** 独有功能 */ public void lookDoor(){ System.out.println("🐕在看🚪!!!"); } }
package com.itheima.d2_polymorphic_advantage; public class Tortoise extends Animal { public String name = "乌龟名称"; @Override public void run() { System.out.println("🐢跑的非常慢~~~"); } }
package com.itheima.d2_polymorphic_advantage; public class Test { public static void main(String[] args) { Animal d = new Dog(); go(d); // d.lookDoor(); Dog类这个子类对象的独有功能无法使用多态这种模式调用 【编译阶段看左边,Animal这个类没有 lookDoor 这个方法,不能调用 子类独有方法】 Animal t = new Tortoise(); go(t); } /** 希望这个方法可以接收一切子类动物对象 * @param a */ public static void go(Animal a){ System.out.println("预备~~~"); a.run(); System.out.println("结束~~~~"); } }
29:多态 之 引用数据类型的类型转换 【自动类型转换】【强制类型转换】
自动类型转换(从子到父):子类对象赋值给父类类型的变量指向 【向上转型,父类在上,子类在下】 Animal c = new Cat();
强制类型转换(从父到子): 从父到子( 必须进行强制类型转换,否则报错): 子类 对象变量 = (子类)父类类型的变量 作用:可以解决多态下的劣势,可以实现调用子类独有的功能。 注意: 有继承/实现关系的类就可以在编译阶段进行强制类型转换;但是,如果转型后的类型和对象真实对象的类型不是同一种类型,那么在运行代码时,就会出现ClassCastException Animal c = new Cat(); Dog d = (Dog)c; // 出现异常 ClassCastException Java建议强转转换前使用 instanceof 判断当前对象的真实类型,再进行强制转换 变量名 instanceof 真实类型 判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是则返回true,反之。
package com.itheima.d3_polymorphic_convert; /** 目标:学习多态形式下的类中转换机制。 */ public class Test { public static void main(String[] args) { // 自动类型转换 Animal a = new Dog(); a.run(); // a.lookDoor(); // 多态下无法调用子类独有功能 // 强制类型转换:可以实现调用子类独有功能的 Dog d = (Dog) a; d.lookDoor(); // 注意:多态下直接强制类型转换,可能出现类型转换异常 // 规定:有继承或者实现关系的2个类型就可以强制类型转换,运行时可能出现问题。 // Tortoise t1 = (Tortoise) a; // 把狗转成乌龟 // 建议强制转换前,先判断变量指向对象的真实类型,再强制类型转换。 【为了避免报错】 if(a instanceof Tortoise){ Tortoise t = (Tortoise) a; t.layEggs(); }else if(a instanceof Dog){ Dog d1 = (Dog) a; d1.lookDoor(); } System.out.println("---------------------"); Animal a1 = new Dog(); go(a1); } public static void go(Animal a){ System.out.println("预备~~~"); a.run(); // 独有功能 if(a instanceof Tortoise){ Tortoise t = (Tortoise) a; t.layEggs(); }else if(a instanceof Dog){ Dog d1 = (Dog) a; d1.lookDoor(); } System.out.println("结束~~~~"); } }
引用数据类型的类型转换,有几种方式? 自动类型转换、强制类型转换。 强制类型转换能解决什么问题? 可以转换成真正的子类类型,从而调用子类独有功能。 强制类型转换需要注意什么? 有继承关系/实现的2个类型就可以进行强制转换,编译无问题。 运行时,如果发现强制转换后的类型不是对象真实类型则报错(ClassCastException) 强制类型转换前最好做什么事情,如何进行? 使用instanceof判断当前对象的真实类型,再进行强制转换 对象变量名 instanceof 真实类型
多态+接口+抽象类综合案例
package com.itheima.d4_polymorphic_test; public interface USB { void connect(); void unconnect(); }
package com.itheima.d4_polymorphic_test; /** 实现类(子类)键盘 */ public class KeyBoard implements USB{ private String name; public KeyBoard(String name) { this.name = name; } @Override public void connect() { System.out.println(name + "成功的接入了设备了~~~"); } @Override public void unconnect() { System.out.println(name + "成功的从设备弹出了~~~"); } /** 独有功能 */ public void keyDown(){ System.out.println(name + "写下了:老铁,6666,下次再来哦,老弟~~~~"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.itheima.d4_polymorphic_test; /** 实现类(子类)鼠标 */ public class Mouse implements USB{ private String name; public Mouse(String name) { this.name = name; } @Override public void connect() { System.out.println(name + "成功的接入了设备了~~~"); } @Override public void unconnect() { System.out.println(name + "成功的从设备弹出了~~~"); } /** 独有功能 */ public void click(){ System.out.println(name + "双击点亮小红心~~~~"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.itheima.d4_polymorphic_test; public class Computer { /** 提供一个安装的入口:行为。 【按照 任何 usb设备,所以传参定义为usb类型】 */ public void installUSB(USB u){ u.connect(); // 独有功能 【强制类型转换才能调用独有功能】 if(u instanceof Mouse){ Mouse m = (Mouse) u; m.click(); }else if(u instanceof KeyBoard) { KeyBoard k = (KeyBoard) u; k.keyDown(); } u.unconnect(); } }
package com.itheima.d4_polymorphic_test; /** 目标:USB设备模拟 1、定义USB接口:接入 拔出 2、定义2个USB的实现类:鼠标、键盘。 3、创建一个电脑对象,创建USB设备对象,安装启动。 */ public class Test { public static void main(String[] args) { // a、创建电脑对象 Computer c = new Computer(); // b、创建USB设备对象 USB u = new Mouse("罗技鼠标"); c.installUSB(u); USB k = new KeyBoard("双飞燕键盘"); c.installUSB(k); } }
30:内部类 静态内部类 对象内部类 局部内部类
内部类:内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。 public class People{ // 内部类 public class Heart{ } } 内部类的使用场景 场景:当一个事物的内部,还有一个部分需要一个完整的结构进行描述时。这个内部的完整结构又只为外部事物提供服务,那么整个内部的完整结构可以自己选择使用内部类设计
【比如 汽车类 里 有个 发动机类 人类 里 心脏类】
基本作用 内部类通常可以方便访问外部类的成员,包括私有的成员。 内部类提供了更好的封装性,内部类本身就可以用 private ,protectecd 等修饰,封装性可以做更多控制【外部类 只能使用 pubulic 和 缺省 修饰】
pubulic:任何地方可以访问
缺省:当前包里可以被访问 内部类的分类 静态内部类[了解] 成员内部类(非静态内部类) [了解] 局部内部类[了解] 匿名内部类(重点)
静态内部类? 有static修饰,属于外部类本身。 它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已。 public class Outer{ // 静态成员内部类 public static class Inner{ } } 静态内部类创建对象的格式: 格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器; 范例:Outer.Inner in = new Outer.Inner(); 静态内部类的访问拓展: 1、静态内部类中是否可以直接访问外部类的静态成员? 可以,外部类的静态成员只有一份可以被共享访问。 2、静态内部类中是否可以直接访问外部类的实例成员? 不可以的,外部类的实例成员必须用外部类对象访问。 静态内部类的使用场景、特点、访问总结。 如果一个类中包含了一个完整的成分,如汽车类中的发动机类。 特点、使用与普通类是一样的,类有的成分它都有,只是位置在别人里面而已。 访问总结:可以直接访问外部类的静态成员,不能直接访问外部类的实例成员。 注意:开发中实际上用的还是比较少。
package com.itheima.d5_innerclass_static; /** 外部类 */ public class Outer { public static int a = 100; private String hobby; /** 学习静态成员内部类 */ public static class Inner{ private String name; // 实例成员变量 private int age; public static String schoolName; // 静态成员变量 public Inner(){} // 无参构造器 public Inner(String name, int age) { // 有参构造器 this.name = name; this.age = age; } public void show(){ // 普通实例方法 System.out.println("名称:" + name); System.out.println(a); // System.out.println(hobby); // 报错!【不能访问外部类的 实例 变量】 // Outer o = new Outer(); // 创建对象可以间接访问 外部类的 实例变量 // System.out.println(o.hobby); } 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; } } }
package com.itheima.d5_innerclass_static; public class Test { public static void main(String[] args) { Outer.Inner in = new Outer.Inner(); in.setName("张三"); in.show(); } }
成员内部类?【也叫 实例 内部类,属于对象】 无static修饰,属于外部类的对象。 JDK16之前,成员内部类中不能定义静态成员,JDK 16开始也可以定义静态成员了。 public class Outer { // 成员内部类 public class Inner { } } 成员内部类创建对象的格式: 格式:外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器();
范例:Outer.Inner in = new Outer().new Inner(); 成员内部类的访问拓展: 1、成员内部类中是否可以直接访问外部类的静态成员? 可以,外部类的静态成员只有一份可以被共享访问。 2、成员内部类的实例方法中是否可以直接访问外部类的实例成员? 可以的,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。
package com.itheima.d6_innerclass; /** 外部类 */ public class Outer { public static int num = 111; private String hobby; public Outer() { } public Outer(String hobby) { this.hobby = hobby; } /** 成员内部类:不能加static修饰 属于外部类对象的 */ public class Inner{ private String name; // 实例成员 private int age; public static int a = 100; // JDK 16开始支持静态成员了 public static void test(){ // 静态方法 System.out.println(a); } public void show(){ // 普通实例方法 System.out.println("名称:" + name); System.out.println("数量:" + num); // 访问外部类的 静态 变量 System.out.println("爱好:" + hobby); // 访问外部类的 成员 变量 } 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; } } }
package com.itheima.d6_innerclass; public class Test { public static void main(String[] args) { Outer.Inner in = new Outer().new Inner(); in.setName("内部"); in.show(); Outer.Inner.test(); // 访问 成员内部类 静态方法 System.out.println("------------"); Outer.Inner in1 = new Outer("爱听课").new Inner(); in1.show(); } }
package com.itheima.d6_innerclass; public class Test2 { public static void main(String[] args) { People.Heart heart = new People().new Heart(); heart.show(); } } class People{ private int heartbeat = 150; /** 成员内部类 */ public class Heart{ private int heartbeat = 110; public void show(){ int heartbeat = 78; System.out.println(heartbeat); // 78 System.out.println(this.heartbeat); // 110 System.out.println(People.this.heartbeat); // 150 访问所寄生 外部类的 对象 } } }
TIPS:在成员内部类中,访问所在的外部类对象, 格式:外部类名.this
局部内部类 (鸡肋语法,了解即可) 局部内部类放在方法、代码块、构造器等执行体中。 局部内部类的类文件名为: 外部类$N内部类.class
package com.itheima.d7_innerclass; /** 目标:了解局部内部类的语法 */ public class Test { static { class Dog{ } abstract class Animal{ } interface SportManInter{ } } public static void main(String[] args) { class Cat{ private String name; public static int onLineNumber = 100; public String getName() { return name; } public void setName(String name) { this.name = name; } } interface SportManInter{ } Cat c = new Cat(); c.setName("叮当猫~"); System.out.println(c.getName()); } }
31:内部类 匿名内部类
匿名内部类: 本质上是一个没有名字的局部内部类。【定义在方法中,代码块中】 作用:方便创建子类对象,最终目的是为了简化代码编写。 格式:【直接创建子类对象需要 定义父类子类继承,然后再创建子类对象,匿名内部类绕过子类这一部分,直接得到子类对象】 new 类|抽象类名|或者接口名() { 重写方法; }; Employee a = new Employee() { public void work() { } }; a. work(); 特点总结: 匿名内部类是一个没有名字的内部类,同时也代表一个对象。 匿名内部类产生的对象类型,相当于是当前new的那个的类型的子类类型 匿名内部类的作用? 方便创建子类对象,最终目的为了简化代码编写。 匿名内部类的格式? Animal a = new Employee() { public void run() { } }; a. run(); 匿名内部类的特点? 匿名内部类是一个没有名字的内部类,【运行编译的时候会产生一个class 文件】
匿名内部类写出来就会产生一个匿名内部类对象 匿名内部类的对象类型,相当于是当前new的那个类型的子类类型。【多态形式的写法】
package com.itheima.d8_innerclass_anonymous; /** 目标:学习匿名内部类的形式和特点。 如下,匿名内部类 不需要依赖 Tiger 这个类实现子类,直接实现 【快速构建对象,不需要实现子类】 */ public class Test { public static void main(String[] args) { Animal a = new Animal(){ @Override public void run() { System.out.println("老虎跑的块~~~"); } }; a.run(); } } //class Tiger extends Animal{ // @Override // public void run() { // System.out.println("老虎跑的块~~~"); // } //} abstract class Animal{ public abstract void run(); }
匿名内部类使用案例:
package com.itheima.d8_innerclass_anonymous; /** 目标:掌握匿名内部类的使用形式(语法) */ public class Test2 { public static void main(String[] args) { Swimming s = new Swimming() { @Override public void swim() { System.out.println("学生快乐的自由泳🏊"); } }; go(s); System.out.println("--------------"); Swimming s1 = new Swimming() { @Override public void swim() { System.out.println("老师泳🏊的贼快~~~~~"); } }; go(s1); System.out.println("--------------"); go(new Swimming() { @Override public void swim() { System.out.println("运动员🏊的贼快啊~~~~~"); } }); } /** 学生 老师 运动员可以一起参加游泳比赛 */ public static void go(Swimming s){ System.out.println("开始。。。"); s.swim(); System.out.println("结束。。。"); } } interface Swimming{ void swim(); }
匿名内部类使用场景:
package com.itheima.d8_innerclass_anonymous; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** 目标:通过GUI编程 理解匿名内部类的真实使用场景 */ public class Test3 { public static void main(String[] args) { // 1、创建窗口 JFrame win = new JFrame("登录界面"); JPanel panel = new JPanel(); // 定义一个桌布 【桌布可以自己布局,默认布局,自动使用按钮大小,窗口不行,窗口直接铺满】 win.add(panel); // 桌布放到 界面 // 2、创建一个按钮对象 JButton btn = new JButton("登录"); // 注意:讲解匿名内部类的使用 【按钮绑定一个监听器,才让别人知道有没有点击操作按钮】 方法入参是一个 ActionListener 接口监听器对象 btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(win, "点我一下,说明爱我!"); } }); // btn.addActionListener( e -> JOptionPane.showMessageDialog(win, "别说话,吻我!!") ); // 3、把按钮对象添加到桌布上展示 panel.add(btn); // 4、展示窗口 win.setSize(400, 300); win.setLocationRelativeTo(null); //居中 win.setVisible(true); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!