今日内容
- static----------必须掌握
- 修饰成员变量
- 修饰成员方法
- 修饰代码块
- 接口----------必须掌握
- 定义接口
- 实现接口
- 接口中成员访问特点
- 多态----------必须掌握------>相对难点
- 实现多态
- 多态时成员访问特点
- 多态的使用场景
- 多态的好处和弊端
- 引用类型转换
- 内部类
- 成员内部类
- 匿名内部类----------必须掌握
- 引用类型使用小结
第一章 static关键字
1.static关键字
1.1 概述
- 概念: static是静态修饰符,表示静态的意思,可以修饰成员变量和成员方法以及代码块。
1.2 修饰成员变量
格式
- 格式:
static 数据类型 变量名;
特点
- 被static修饰的成员变量叫做静态成员变量\类变量
- 被static修饰的成员变量会被该类的所有对象共享
- 被static修饰的成员变量被该类的某个对象修改了,那么该类的所有对象使用的都是修改后的值
访问方式
- 方式一:
对象名.静态成员变量名
---->不推荐 - 方式二:
类名.静态成员变量名
----->推荐
案例:
public class Chinese {
String name;// 姓名
static String country;// 国籍
public Chinese(String name, String country) {
this.name = name;
this.country = country;
}
public Chinese() {
}
}
public class Test {
public static void main(String[] args) {
/*
##### 格式
- 格式: `static 数据类型 变量名;`
##### 特点
- 被static修饰的成员变量叫做静态成员变量\类变量
- 被static修饰的成员变量会被该类的所有对象共享
- 被static修饰的成员变量被该类的某个对象修改了,那么该类的所有对象使用的都是修改后的值
##### 访问方式
- 方式一: `对象名.静态成员变量名` ---->不推荐
- 方式二: `类名.静态成员变量名` ----->推荐
*/
// 给静态成员变量country赋值
Chinese.country = "中国";
// 创建Chinese对象
Chinese c1 = new Chinese();
// null,中国
System.out.println(c1.name+","+c1.country);
System.out.println("----------------");
// 创建Chinese对象
Chinese c2 = new Chinese();
// null,中国
System.out.println(c2.name+","+c2.country);
System.out.println("----------------");
// 使用c2修改静态成员变量country的值
c2.country = "中华人民共和国";
// 中华人民共和国,中华人民共和国
System.out.println(c1.country+","+c2.country);
}
}
1.3 修饰成员方法
格式
- 格式:
修饰符 static 返回值类型 方法名(形参列表){ 方法体 };
特点
- 被static修饰的成员方法叫做静态方法,可以使用类名直接调用
访问方式
- 方式一:
对象名.静态方法名(实参);
------->推荐 - 方式二:
类名.静态方法名(实参);
------->推荐
案例:
public class Chinese {
// 非静态成员方法
public void method(){
System.out.println("非静态成员方法 method...");
}
// 静态成员方法
public static void show(){
System.out.println("静态成员方法 show...");
}
}
public class Test {
public static void main(String[] args) {
// 方式一
Chinese c = new Chinese();
c.show();
// 方式二
Chinese.show();
}
}
1.4 静态方法调用的注意事项
-
静态方法中不能出现this关键字
-
静态方法中只能直接访问静态成员变量和静态成员方法
-
静态方法中不能直接访问非静态成员变量和非静态成员方法
-
非静态方法中可以直接访问一切成员变量和成员方法
public class Chinese { String name; static String country; public void show1(){ System.out.println("非静态成员方法"); } public void show2(){ //- 非静态方法中可以直接访问一切成员变量和成员方法 System.out.println(name);// 非静态成员变量 System.out.println(country);// 静态成员变量 show1();// 非静态成员方法 method2();// 静态成员方法 } public static void method1(){ //- 静态方法中不能出现this关键字 //System.out.println(this.name);// 编译报错 //- 静态方法中不能直接访问非静态成员变量和非静态成员方法 //System.out.println(name);// 编译报错 //show1();// 编译报错 //- 静态方法中只能直接访问静态成员变量和静态成员方法 System.out.println(country); method2(); } public static void method2(){ System.out.println("静态成员方法"); } }
public class Test { public static void main(String[] args) { //Chinese.method1(); new Chinese().show2(); } }
1.5 修饰代码块
格式: 静态代码块
static {
}
位置
- 类中方法外
执行
- 随着类的加载而执行,并且只执行一次
- 类的加载: 第一次使用某个类的时候,类加载器就会把该类加载到内存中,只会加载一次
使用场景
- 如果某些代码只需要执行一次,就可以放在静态代码块中
- eg: 加载驱动, 读配置文件......
案例
public class Chinese {
// 静态代码块
static {
System.out.println("Chinese 静态代码块...");
}
public Chinese() {
System.out.println("Chinese 构造方法...");
}
}
public class Test {
public static void main(String[] args) {
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
}
}
1.6 以后开发中static的应用
概述
以后的项目中,通常会需要一些“全局变量”或者“全局的工具方法”,这些全局变量和方法,可以单独定义在一个类中,并声明为static(静态)的,可以很方便的通过类名访问
例如
public class Utils {
public static final String USERNAME = "root";
public static final String PASSWORD = "123456";
public static final int WIDTH = 600;
public static final int HEIGHT = 600;
// 工具方法
public static int getArrMax(int[] arr){
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max){
max = arr[i];
}
}
return max;
}
}
第二章 接口
3.1 概述
- 概述: 接口也是一种引用数据类型,在接口中专门用来定义方法和常量
- 接口中的成员:
- 常量(jdk7及其以前)
- 抽象方法(jdk7及其以前)
- 默认方法(有方法体的方法)(jdk8及其以上)
- 静态方法(jdk8及其以上)
- 私有方法(jdk9及其以上)
- 定义一个接口需要使用
interface
关键字---->接口编译之后会产生class文件 - 接口不能创建对象,只能被类实现(类似继承),实现接口的类叫做实现类\子类
- 实现接口的类如果是普通类,就必须重写接口中所有的抽象方法,如果是抽象类,就可以不重写,也可以重写
3.2 定义格式
格式
修饰符 interface 接口名{
- 常量(jdk7及其以前)
- 抽象方法(jdk7及其以前)
- 默认方法(有方法体的方法)(jdk8及其以上)
- 静态方法(jdk8及其以上)
- 私有方法(jdk9及其以上)
}
案例
public interface A {
// - 常量(jdk7及其以前) 使用public static final关键字修饰,这3个关键字可以省略不写
public static final int NUM = 10;
// - 抽象方法(jdk7及其以前) 使用public abstract关键字修饰,这2个关键字可以省略不写
public abstract void method1();
// - 默认方法(有方法体的方法)(jdk8及其以上)
// 使用public default关键字修饰,其中public可以省略不写,default不可以省略
public default void method2(){
System.out.println("A接口 默认方法...");
}
// - 静态方法(jdk8及其以上) 使用public static关键字修饰,其中public可以省略,static不可以省略
public static void method3(){
System.out.println("A接口 静态方法...");
}
// - 私有方法(jdk9及其以上) 使用private关键字修饰
/*
private void mehtod4(){
}
private static void mehtod5(){
}
*/
}
3.3 实现接口
实现概述
- 概述:类和接口的关系就是实现关系,类似继承,使用implements关键字来实现接口
实现格式:
-
单实现
修饰符 class 类名 implements 接口名{}
-
多实现
修饰符 class 类名 implements 接口名1,接口名2,接口名3,...{}
-
先继承后实现
修饰符 class 类名 extends 父类名 implements 接口名1,接口名2,接口名3,...{}
案例演示:
interface A{}
interface B{}
class Fu{}
// 单实现
class Imp1 implements A{}
// 多实现
class Imp2 implements A,B{}
// 先继承后实现
class Imp3 extends Fu implements A,B{}
public class Test {
public static void main(String[] args) {
}
}
3.4 接口中成员的访问特点
-
访问特点:
接口中成员的访问特点: 常量: 主要是供接口名直接访问 抽象方法: 就是供实现类重写的 默认方法: 就是供实现类重写或者直接调用 静态方法: 只供接口名直接访问 私有方法: 只能在接口内部访问
-
案例:
class Imp implements A{ @Override public void method1() { System.out.println("实现类 重写method1抽象方法"); } @Override public void method2() { System.out.println("实现类 重写method2默认方法"); } } public class Test { public static void main(String[] args) { /* 接口中成员的访问特点: 常量: 主要是供接口名直接访问 抽象方法: 就是供实现类重写的 默认方法: 就是供实现类重写或者直接调用 静态方法: 只供接口名直接访问 私有方法: 只能在接口内部访问 */ // 访问接口中的常量 System.out.println(A.NUM);// 推荐 System.out.println(Imp.NUM);// 不推荐 // 访问接口中的抽象方法 Imp imp = new Imp(); imp.method1(); // 访问接口中的默认方法 imp.method2(); // 访问接口中的静态方法 A.method3(); } }
public interface A { // - 常量(jdk7及其以前) 使用public static final关键字修饰,这3个关键字可以省略不写 public static final int NUM = 10; // - 抽象方法(jdk7及其以前) 使用public abstract关键字修饰,这2个关键字可以省略不写 public abstract void method1(); // - 默认方法(有方法体的方法)(jdk8及其以上) // 使用public default关键字修饰,其中public可以省略不写,default不可以省略 public default void method2(){ System.out.println("A接口 默认方法..."); } // - 静态方法(jdk8及其以上) 使用public static关键字修饰,其中public可以省略,static不可以省略 public static void method3(){ System.out.println("A接口 静态方法..."); } // - 私有方法(jdk9及其以上) 使用private关键字修饰 /* private void mehtod4(){ } private static void mehtod5(){ } */ }
3.5 多实现时的几种冲突情况
公有静态常量的冲突
- 如果实现类实现多个接口,这多个接口中有相同的常量,那么实现类是不会继承的
interface A{
public static final int A = 10;
public static final int B = 10;
}
interface B{
public static final int A = 20;
}
class Imp implements A,B{
}
public class Test {
public static void main(String[] args) {
//System.out.println(Imp.A);// 编译报错
System.out.println(Imp.B);
}
}
公有抽象方法的冲突
interface A{
public abstract void method();
}
interface B{
public abstract void method();
}
class Imp implements A,B{
// 如果实现的多个父接口中有相同的抽象方法,实现类只需要重写一次即可
@Override
public void method() {
}
}
public class Test {
public static void main(String[] args) {
}
}
公有默认方法的冲突
interface A{
public default void method(){
System.out.println("A接口 默认方法method...");
}
}
interface B{
public default void method(){
System.out.println("B接口 默认方法method...");
}
}
class Imp implements A,B{
// 如果实现的多个父接口中有相同的默认方法,实现类必须重写该默认方法
@Override
public void method() {
System.out.println("Imp实现类 重写的默认方法method...");
}
}
public class Test {
public static void main(String[] args) {
new Imp().method();
}
}
公有静态方法的冲突
interface A{
public static void method(){
System.out.println("A接口 静态方法method...");
}
}
interface B{
public static void method(){
System.out.println("B接口 静态方法method...");
}
}
class Imp implements A, B {
// 不报错,因为接口中的静态方法,实现类无法继承
}
public class Test {
public static void main(String[] args) {
}
}
私有方法的冲突
- 不存在冲突,因为私有方法不能被实现类继承
3.6 接口和接口的关系
接口与接口之间的关系
-
接口和接口之间是继承关系
-
单继承
修饰符 interface 子接口名 extends 父接口名{}
-
多继承
修饰符 interface 子接口名 extends 父接口名1,父接口名2,...{}
-
多层继承
修饰符 interface 父接口名 extends 爷爷接口名{} 修饰符 interface 子接口名 extends 父接口名{}
接口继承接口的冲突情况
公有静态常量的冲突
interface A{
public static final int A = 10;
public static final int B = 10;
}
interface B{
public static final int A = 10;
}
interface C extends A,B{
}
public class Test {
public static void main(String[] args) {
// 子接口可以继承父接口中的常量,如果多个父接口中有相同的常量,那么子接口就不会继承这些相同的常量
//System.out.println(C.A);// 编译报错
System.out.println(C.B);
}
}
公有抽象方法冲突
interface A{
public abstract void method();
}
interface B{
public abstract void method();
}
interface C extends A,B{
}
class Imp implements C{
// 如果多个父接口中有相同的抽象方法,子接口只会继承一个,所以实现类只需要重写一次即可
@Override
public void method() {
}
}
public class Test {
}
公有默认方法的冲突
interface A{
public default void method(){
System.out.println("A接口 默认方法method...");
}
}
interface B{
public default void method(){
System.out.println("B接口 默认方法method...");
}
}
interface C extends A,B{
// 如果多个父接口中有相同的默认方法,那么子接口必须重写一次该默认方法
@Override
default void method() {
System.out.println("C接口 重写父接口中method默认方法...");
}
}
class Imp implements C{
}
public class Test {
public static void main(String[] args) {
new Imp().method();
}
}
公有静态方法和私有方法
- 不存在冲突,因为静态方法和私有方法不会被子接口继承
3.7 继承的同时又实现存在的问题
父类和接口的公有静态常量的冲突
interface A{
public static final int NUM = 10;
}
class Fu{
public static final int NUM = 10;
}
class Zi extends Fu implements A{
}
public class Test {
public static void main(String[] args) {
// 子类不会继承的父类和父接口中相同的常量
//System.out.println(Zi.NUM);// 编译报错
}
}
父类和接口的抽象方法冲突
interface A {
public abstract void method();
}
abstract class Fu {
public abstract void method();
}
class Zi extends Fu implements A {
@Override
public void method() {
// 如果父接口和父类中有相同的抽象方法,子类必须重写一次
}
}
public class Test {
}
父类和接口的公有默认方法的冲突
interface A {
public default void method(){
System.out.println("A接口 method默认方法");
}
}
abstract class Fu {
public void method(){
System.out.println("Fu类 method默认方法");
}
}
class Zi extends Fu implements A {
}
public class Test {
public static void main(String[] args) {
// 如果父类和父接口中有相同的默认方法,子类会优先访问父类中的默认方法
new Zi().method();
}
}
父类和接口的公有静态方法冲突
interface A {
public static void method(){
System.out.println("A接口 method静态方法");
}
}
abstract class Fu {
public static void method(){
System.out.println("Fu类 method静态方法");
}
}
class Zi extends Fu implements A {
}
public class Test {
public static void main(String[] args) {
// 父接口中的静态方法不会被实现类继承,而父类中的静态方法是会被子类继承
Zi.method();
}
}
父类和接口的私有方法
- 不存在冲突
3.8 抽象类和接口的练习
接口和抽象类的区别
- 接口和抽象类中的成员不同(........)
- 使用场景不同:
- 抽象类一般是作为同一类事物的父类
- 接口可以作为不同一类事物的父接口
- 继承抽象父类只能继承一个,而实现接口可以实现多个接口
需求:
通过实例进行分析和代码演示抽象类和接口的用法。
1、举例:
犬: 作为抽象父类
行为:吼叫;吃饭;
缉毒犬: 继承犬类
行为:吼叫;吃饭;缉毒;
抽象类: 如果同一群事物共同的功能就定义在抽象类中
接口: 如果不同类型的事物共同拥有的功能就定义在接口中,让这些事物的类实现该接口
实现:
-
Dog
public abstract class Dog { public abstract void houJiao(); public abstract void eat(); }
-
JiDu
public interface JiDu { public abstract void jiDu(); }
-
JiDuDog
public class JiDuDog extends Dog implements JiDu{ public void houJiao(){ System.out.println("旺旺旺..."); } public void eat(){ System.out.println("狗在吃骨头..."); } public void jiDu(){ System.out.println("狗在缉毒..."); } }
-
Test
public class Test { public static void main(String[] args) { JiDuDog jiDuDog = new JiDuDog(); jiDuDog.eat(); jiDuDog.jiDu(); jiDuDog.houJiao(); } }
第三章 多态
3.1 概述
- 多态是面向对象三大特征之一。
- 多态指对于同一行为,对于不同的对象,具有不同的表现形式。在程序中,表示对同一方法,不同的对象有不同实现。
- 实现多态的前提条件
- 继承 或者 实现
- 父类引用指向子类对象 或者 父接口引用指向实现类对象
- 方法的重写(没有重写方法多态是没有意义的)
3.2 实现多态
-
继承时的多态:
class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼..."); } } public class Test { public static void main(String[] args) { // 父类引用指向子类的对象 Animal anl = new Dog(); anl.eat(); anl = new Cat(); anl.eat(); } }
-
实现时的多态:
interface A{ public abstract void method(); } class Imp1 implements A{ @Override public void method() { System.out.println("Imp1 重写method方法..."); } } class Imp2 implements A{ @Override public void method() { System.out.println("Imp2 重写method方法..."); } } public class Test { public static void main(String[] args) { // 父接口的引用指向实现类的对象 A a = new Imp1(); a.method(); a = new Imp2(); a.method(); } }
3.3 多态时访问成员的特点
-
特点:
- 成员变量: 编译看左边(父类),运行看左边(父类)
- 成员方法:
- 非静态成员方法: 编译看左边(父类),运行看右边(子类)
- 静态成员方法: 编译看左边(父类),运行看左边(父类)
-
代码:
class Fu{ int num = 20; public void method1(){ System.out.println("Fu 非静态成员方法method1..."); } public static void method2(){ System.out.println("Fu 静态成员方法method2..."); } } class Zi extends Fu{ int num = 10; @Override public void method1(){ System.out.println("Zi 非静态成员方法method1..."); } public static void method2(){ System.out.println("Zi 静态成员方法method2..."); } } public class Test { public static void main(String[] args) { // 父类的引用指向子类的对象 Fu f = new Zi(); // 多态时访问成员变量:编译看左边(父类),运行看左边(父类) System.out.println(f.num);// 20 // 多态时访问非静态成员方法: 编译看左边(父类),运行看右边(子类) f.method1(); // 多态时访问静态成员方法: 编译看左边(父类),运行看左边(父类) f.method2(); } }
3.4 多态的几种表现形式
-
普通父类多态
class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } public class Test { public static void main(String[] args) { // 父类的引用指向子类的对象 Animal anl = new Dog(); anl.eat(); } }
-
抽象父类多态
abstract class Animal{ public abstract void eat(); } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } public class Test { public static void main(String[] args) { // 父类的引用指向子类的对象 Animal anl = new Dog(); anl.eat(); } }
-
父接口多态
interface Animal{ public abstract void eat(); } class Dog implements Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } public class Test { public static void main(String[] args) { // 父接口的引用指向实现类的对象 Animal anl = new Dog(); anl.eat(); } }
3.5 多态的应用场景
-
变量多态 -----> 意义不大
class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼..."); } } public class Test { public static void main(String[] args) { // 变量多态: 变量的数据类型为父类类型,接收该父类类型的子类对象 Animal anl = new Dog(); anl.eat(); anl = new Cat(); anl.eat(); } }
-
形参多态----> 常用
-
结论: 参数的类型是父类类型,那么就可以接收该父类类型的对象以及该父类类型的所有子类对象
class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼..."); } } public class Test { public static void main(String[] args) { // 创建Animal对象,调用method方法 Animal anl = new Animal(); method(anl); // 创建Dog对象,调用method方法 Dog dog = new Dog(); method(dog); // 创建Cat对象,调用method方法 Cat cat = new Cat(); method(cat); } // 定义一个方法,既可以接收Animal类的对象以及Animal类的所有子类对象 // 传入Dog类对象,实参给形参赋值,其实就是: Animal anl = new Dog(); // 传入Cat类对象,实参给形参赋值,其实就是: Animal anl = new Cat(); // 结论: 如果方法的形参类型为父类类型,那么可以接收该父类类型对象或者其所有子类对象 public static void method(Animal anl){ anl.eat(); } /*public static void method(Dog dog){ } public static void method(Cat cat){ } // ...*/ }
-
-
返回值多态---> 常用
- 返回值类型为父类类型,那么就可以返回该父类类型的对象,或者该父类类型的子类对象
class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼..."); } } public class Test { public static void main(String[] args) { // 当返回值返回的时候,其实就是执行: Animal anl = new Animal(); Animal anl = getAnimal("Animal"); anl.eat(); System.out.println("-------------"); // 当返回值返回的时候,其实就是执行: Animal anl1 = new Dog(); Animal anl1 = getAnimal("Dog"); anl1.eat(); System.out.println("-------------"); // 当返回值返回的时候,其实就是执行: Animal anl2 = new Cat(); Animal anl2 = getAnimal("Cat"); anl2.eat(); } // 定义一个方法,既可以返回Animal对象,又可以返回Animal类的所有子类对象 public static Animal getAnimal(String type){ if ("Dog".equals(type)){ // 返回的是Dog对象 return new Dog(); }else if ("Cat".equals(type)){ // 返回的是Cat对象 return new Cat(); }else if ("Animal".equals(type)){ // 返回的是Animal对象 return new Animal(); }else{ return null; } } }
3.6 多态的好处和弊端
-
好处:
- 可以将方法的参数定义为父类引用,使程序编写的更简单,提高程序的灵活性,扩展性
- 案例参考多态的应用场景
-
弊端:
-
无法直接访问子类独有的成员变量和成员方法(因为多态时成员访问特点编译都是看左边(父类))
-
案例:
class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } public void lookHome(){ System.out.println("狗在看家..."); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼..."); } public void catchMouse(){ System.out.println("猫抓老鼠..."); } } public class Test { public static void main(String[] args) { // 无法直接访问子类独有的成员变量和成员方法(因为多态时成员访问特点编译都是看左边(父类)) // 父类的引用指向子类的对象 Animal anl = new Dog(); anl.eat(); //anl.lookHome();// 编译报错,因为编译看左边(父类),而父类没有这个方法,所以编译报错 anl = new Cat(); anl.eat(); //anl.catchMouse();// 编译报错,因为编译看左边(父类),而父类没有这个方法,所以编译报错 } }
-
3.7 引用类型转换
-
为什么要转换
- 向上转型为了实现多态
- 向下转型为了解决多态的弊端
-
如何转型
- 向上转型: 子类类型向父类类型转换的过程叫做向上转型,这个过程是自动的
- 格式:
父类类型 变量名 = 子类对象;
- eg:
Fu f = new Zi();
- 格式:
- 向下转型: 父类类型的变量向子类类型转换的过程叫做向下转型,这个过程是强制的
- 格式:
子类类型 变量名 = (子类类型)父类类型的变量;
- eg:
Zi zi = (Zi)f;
- 注意:
- 向下转型要求右边父类类型的变量指向的对象一定是属于左边子类类型,否则会报类型转换异常ClassCastException
- 格式:
- 向上转型: 子类类型向父类类型转换的过程叫做向上转型,这个过程是自动的
-
避免转型异常
-
转型判断格式
if(变量名 instanceof 数据类型){} 如果前面变量名指向的对象是属于后面数据类型,那么返回true,否则返回false
-
-
案例:
class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } public void lookHome(){ System.out.println("狗在看家..."); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼..."); } public void catchMouse(){ System.out.println("猫抓老鼠..."); } } public class Test { public static void main(String[] args) { // 父类的引用指向子类的对象 Animal anl = new Dog(); anl.eat(); //anl.lookHome();// 编译报错,因为编译看左边(父类),而父类没有这个方法,所以编译报错 // 转型之前应该判断anl指向的是否是Cat类型的对象 if (anl instanceof Dog) { // 向下转型 Dog dog = (Dog) anl; dog.lookHome(); } System.out.println("-------------"); anl = new Cat(); anl.eat(); //anl.catchMouse();// 编译报错,因为编译看左边(父类),而父类没有这个方法,所以编译报错 // 转型之前应该判断anl指向的是否是Cat类型的对象 if (anl instanceof Cat) { // 向下转型 Cat cat = (Cat) anl; cat.catchMouse(); } System.out.println("-------------"); } }
第四章 内部类
4.1 内部类的概述
- 概述: 将一个类定义在另一个类的里面,里面的那个类就叫做内部类,外面的那个类就叫做外部类
- 特点: 内部类是一个独立的类,在编译后,有自己独立的class文件,前面冠以:外部类名+$+内部类类名.class
- 分类:
- 成员内部类
- 局部内部类(自己了解)
- 匿名内部类
4.2 成员内部类
-
概述: 定义在类中方法外的类就叫做成员内部类,是外部类的一个成员
-
格式:
public class 外部类名{ public class 内部类名{ } }
-
成员内部类的成员访问特点:
-
特点: 想要在其他类中访问成员内部类的成员变量和成员方法,需要创建成员内部类对象
-
格式:
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
-
案例:
public class Body {// 外部类 public class Heart{// 成员内部类 // 成员变量 String name; // 构造方法 public Heart(String name) { this.name = name; } public Heart() { } // 成员方法 public void methodN1(){ System.out.println("成员内部类的成员方法methodN1..."); } } }
public class Test { public static void main(String[] args) { // 外部类名.内部类名 对象名 = new 外部类名().new 内部类名(); // 创建Body外部类中的Heart内部类对象 Body.Heart bh = new Body().new Heart(); // 访问成员内部类的成员变量 System.out.println(bh.name); // 访问成员内部类的成员方法 bh.methodN1(); } }
-
-
注意事项:
-
在成员内部类中,可以直接访问外部类的一切成员,包括外部类的私有成员
-
在外部类中,访问内部类的成员,需要创建内部类对象来访问
public class Body {// 外部类 // 成员变量 String name = "外"; private int age = 10; // 成员方法 public void methodW1(){ System.out.println("外部类的成员方法methodW1...."); } public class Heart{// 成员内部类 // 成员变量 String name = "内"; // 构造方法 public Heart(String name) { this.name = name; } public Heart() { } // 成员方法 public void methodN1(){ System.out.println("成员内部类的成员方法methodN1..."); } // 在成员内部类中,可以直接访问外部类的一切成员,包括外部类的私有成员 public void methodN2(){ System.out.println("成员内部类的成员方法methodN2..."); String name = "局"; System.out.println("局部的name:"+name); System.out.println("内部类的name:"+this.name); System.out.println("外部类的name:"+Body.this.name); System.out.println("外部类的age:"+age); methodW1(); } } // - 在外部类中,访问内部类的成员,需要创建内部类对象来访问 public void methodW2(){ System.out.println("外部类的成员方法methodW2...."); // 创建内部类对象 //Body.Heart bh = new Body().new Heart(); Heart h = new Heart(); System.out.println(h.name); h.methodN1(); } }
public class Test { public static void main(String[] args) { // 外部类名.内部类名 对象名 = new 外部类名().new 内部类名(); // 创建Body外部类中的Heart内部类对象 Body.Heart bh = new Body().new Heart(); // 访问成员内部类的成员变量 System.out.println(bh.name); // 访问成员内部类的成员方法 bh.methodN1(); bh.methodN2(); System.out.println("-----"); new Body().methodW2(); } }
-
4.3 匿名内部类(重点)
-
概述
- 本质其实就是一个继承了某个类的子类对象
- 本质其实就是一个实现了某个接口的实现类对象
-
格式
new 类名(){ 重写父类中所有抽象方法 }; new 接口名(){ 重写接口中所有抽象方法 };
-
使用场景
- 如果想得到某个类的子类对象,就可以创建该类的匿名内部类
- 如果想得到某个接口的实现类对象,就可以创建该接口的匿名内部类
-
作用:
- 匿名内部类没有特殊的功能,就是来简化代码的(不用创建子类,而是直接得到子类对象)
-
案例1:
abstract class Animal{ public abstract void eat(); } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } } public class Test { public static void main(String[] args) { // 需求:调用Animal类中的eat方法 // 创建Animal类的Dog子类对象 Animal anl1 = new Dog(); anl1.eat(); // 使用Animal类的匿名内部类 ==> Animal类的匿名子类对象 Animal anl2 = new Animal() { @Override public void eat() { System.out.println("匿名内部类的eat方法..."); } }; anl2.eat(); } }
-
案例2:
interface A{ void method(); } class Imp implements A{ @Override public void method() { System.out.println("实现类重写method方法..."); } } public class Test { public static void main(String[] args) { // 需求: 调用A接口中的method方法 // 创建A接口的Imp实现类的对象 A a1 = new Imp(); a1.method(); // 创建A接口的匿名内部类 ===> A接口的匿名实现类对象 A a2 = new A() { @Override public void method() { System.out.println("匿名内部类重写的method方法..."); } }; a2.method(); } }
第五章 引用类型使用小结
5.1 引用类型作为方法参数和返回值
-
引用类型作为方法的参数传递的是地址值
-
引用类型作为方法的返回值返回的是地址值
-
案例1
class Person { String name; public Person(String name) { this.name = name; } } public class Test { public static void main(String[] args) { // - 引用类型作为方法的参数传递的是地址值 //- 引用类型作为方法的返回值返回的是地址值 // 需求: 调用method1方法 // 创建Person对象 Person p1 = new Person("itheima"); // 调用method1方法,传入Person对象 method1(p1); System.out.println("p1的name:" + p1.name);// itcast } public static void method1(Person p) { p.name = "itcast"; } }
-
案例2
class Person { String name; public Person(String name) { this.name = name; } } public class Test { public static void main(String[] args) { //- 引用类型作为方法的返回值返回的是地址值 // 需求: 调用method2方法 // 创建Person对象 Person p2 = new Person("java"); // 调用method2方法,传入Person对象 Person person = method2(p2); System.out.println("person的name:"+person.name); // itcast System.out.println("p2的name:"+p2.name);// itcast } public static Person method2(Person p){ p.name = "itcast"; return p; } }
5.2 引用类型作为成员变量
abstract class Pet{
public abstract void happy();
}
interface FaShu{
public abstract void fly();
}
class Dog extends Pet{
@Override
public void happy() {
System.out.println("宠物给我快乐...");
}
}
class Imp implements FaShu{
@Override
public void fly() {
System.out.println("飞行...");
}
}
class Person{
// 姓名
String name;
// 宠物
Pet pet;
// 法术
FaShu faShu;
public Person() {
}
public Person(String name, Pet pet, FaShu faShu) {
this.name = name;
this.pet = pet;
this.faShu = faShu;
}
}
public class Test {
public static void main(String[] args) {
// 引用类型: 数组,类,接口,...
// 创建Person类对象
Person p = new Person();
// 给p对象的属性赋值
p.name = "张三";
p.pet = new Dog();
p.faShu = new Imp();
// 访问p对象宠物的happy方法
p.pet.happy();
// 访问p对象法术的fly方法
p.faShu.fly();
// 链式编程
}
}
总结
必须练习:
1.static修饰成员变量的格式和使用
2.static修饰成员方法的格式和使用
3.static修饰代码块的格式和使用
4.定义接口,实现接口,接口中成员的访问特点
5.实现多态
6.多态场景下成员访问特点
7.多态的应用场景---->形参多态,返回值多态 重点重点
8.解决多态的弊端--->向下转型,转型判断
9.匿名内部类----> 本质,格式
- 能够使用static修饰成员变量
格式: static 数据类型 变量名
使用: 类名.变量名;
特点: 被该类的所有对象共享
- 能够使用static修饰成员方法
格式: 方法的返回值类型前面加static
使用: 类名.方法名(实参);
- 能够使用static修饰静态代码块
格式: static{}
位置: 类中方法外
执行: 随着类的加载而执行,并且只执行一次,优先于构造方法执行
- 能够写出接口的定义格式
格式: public interface 接口名{}
- 能够写出接口的实现格式
格式: 单实现,多实现,先继承后实现
public class 实现类名 implements 接口名{}
public class 实现类名 implements 接口名1,接口名,...{}
public class 实现类名 extends 父类 implements 接口名1,接口名,...{}
- 能够说出接口中的成员特点
接口中的常量: 主要供接口名直接访问,当然也可以被实现类继承
接口中的抽象方法: 就是供实现类重写的
接口中的默认方法: 可以供实现类对象直接继承调用,也可以供实现类重写
接口中的静态方法: 只能供接口名直接访问,不能被实现类继承
接口中的私有方法: 只能在接口内部直接访问,不能被实现类继承
- 能够说出多态的前提
继承\实现
父类的引用指向子类的对象\接口的引用指向实现类的对象
方法的重写
- 能够写出多态的格式
父类类型 变量名 = new 子类类型(实参);
接口类型 变量名 = new 实现类类型(实参);
- 能够理解多态向上转型和向下转型
向上转型: 父类类型 变量名 = new 子类类型(实参); 存在的意义就是为了实现多态
向下转型: 子类类型 变量名 = (子类类型)父类类型的变量; 存在的意义就是为了解决多态的弊端
注意: 父类类型的变量一定要指向左边子类类型的对象,否则会报类型转换异常ClassCastException
避免转型异常: 变量名 instanceof 数据类型
- 能够说出内部类概念
一个类中包含另一个类,被包含的类就是内部类
- 能够理解匿名内部类的编写格式
本质: 表示一个类的子类对象,或者一个接口的实现类对象
格式:
new 类名(){重写抽象方法};
new 接口名(){重写抽象方法};