目录
1、复用类
提高代码复用性是Java具备的功能之一。Java中有两种方式实现该目的的方法。第一种是在新的类中产生现有类的对象,新的类是有现有的对象所组成,这就是组合。例如:描述汽车类,汽车应该是有车轮子,车窗,发动机等对象组成。
第二种方法是按照现有类的类型创建新类,这就是继承。例如,已知一个Person类,需要定义两个类,分别描述学生,工人。这些类有很多的共性(都是Person),可以通过继承来设计这些类,提高代码的复用性。
1.1 类之间的关系
组合与继承
1.2 组合
1:现实生活
笔记本包含 cpu 整体与部分的关系
航母编队 包含(航母 护卫舰 驱逐舰 舰载机 潜艇)
Java中创建Person对象,人出生应该具备出生地的。所以在描述Person类时,需要一个Address类。
1:描述Person类,描述地址(Address)类,Person类需要维护Address
在Person类中只需要维护一个Address类的引用即可。
class Person{ String name; int age; Address add; Person(){ } Person(String name,int age,Address add){ this.name=name; this.age=age; this.add=add; }
void speak(){ System.out.println("姓名:"+name+" 年龄:"+age+" "+add.print()); } } class Address{ String country; String city; String street; Address(){ } Address(String country,String city,String street){ this.country=country; this.city=city; this.street=street; } String print(){ return "地址:"+country+" "+"城市:"+city+" 街道;"+street; } } class Demo3 public static void main(String[] args){ Address add=new Address("中国","广州","棠东东路"); Person p=new Person("jack",27,add); p.speak(); System.out.println(); } } |
1.3 继承
1:现实生活中一些事物之间是有一定的关系的。例如:学生是人,猫是猫科动物。
Java中的体现
1:描述一个学生类
1:姓名年龄属性,学习的方法
2:描述一个工人类
1:姓名年龄属性,工作的方法
3:描述一个人类
1:姓名年龄属性,说话的方法。
4:发现学生类和人类有关系,学生和工人都是人。所以人有的属性和行为学生和工人都会有。出现了类与类之间代码重复
class Person { String name; int age; // 静态变量(类变量)对象和对象之间的代码重复使用静态变量 static String country = "CN"; Person() { } void speak() { System.out.println(name + ":哈哈,我是人!!!"); } } // 让学生类和人类产生关系,发现学生is a 人,就可以使用继承 class Student { String name; int age; Student() { } void study() { System.out.println("姓名:" + name + "年纪:" + age + ":好好学习"); } } class Worker { String name; int age; void work() { System.out.println(name + ":好好工作,好好挣钱。"); } } class Demo1 { public static void main(String[] args) { Student s = new Student(); s.name = "jack"; s.age = 20; s.study(); Worker w = new Worker(); w.name = "rose"; w.work(); } } |
总结:发现问题:
1:出现类和类有关系
它们之间的关系无法描述。
具备共性的代码出现重复定义。
1.4 继承特点
1:描述类和类之间的关系
2:降低类和类之间的重复代码
1:降低对象和对象之间的代码重复使用静态变量
2:降低类和类之间的代码重复使用就继承
但是一定要符合is a 的关系。
1.5 extends关键字
继承是所有的OOP语言和Java语言中不可缺少的组成部分。
Java中的继承使用extends关键字实现。
1:学生是人,工人是人。
2:谁继承谁?
学生继承人,发现学生里的成员变量,姓名和年龄,人里边也都进行了定义。有重 复代码将学生类的重复代码注释掉,创建学生类对象,仍然可以获取到注释的成员。这就是因为继承的关系,学生类(子类)继承了人类(父类)的部分
class Person { String name; int age; // 静态变量(类变量)对象和对象之间的代码重复使用静态变量 static String country = "CN"; Person() { } void speak() { System.out.println(name + ":哈哈,我是人!!!"); } } // 让学生类和人类产生关系,发现学生is a 人,就可以使用继承 class Student extends Person { Student() { } void study() { System.out.println("姓名:" + name + "年纪:" + age + ":好好学习"); } } class Worker extends Person { void work() { System.out.println(name + ":好好工作,好好挣钱。"); } } class Demo1 { public static void main(String[] args) { Student stu = new Student(); stu.name = "jack"; stu.age = 20; stu.study(); stu.speak(); System.out.println(stu.country); System.out.println(Student.country); Worker worker = new Worker(); worker.name = "rose"; System.out.println(worker.country); worker.work(); worker.speak(); System.out.println(); } } |
案例:测试继承
子类并能否继承父类中所有的成员?
提示:
1:父类定义完整的成员 静态成员变量,非静态成员变量(私有和非私有),构造方法(有参数构造无参数构造)静态方法,非静态成员方法。
2:创建子类继承父类。
尝试在子类中访问父类的private成员变量。
尝试一个子类继承多个父类。
3:创建子类对象,尝试通过子类对象名来访问父类的成员。
结论:
子类并不能继承父类中所有的成员
1.父类的所有的私有成员不能继承(private修饰的成员)
2.构造函数不能被继承
3.Java只支持单继承,不支持多继承。
3:如何使用继承
继承的出现是为了描述is a关系出现的,不要为了仅仅使用某个类的方法,就盲目的继承一个类。一个父类和它的子类之间必须是存在关系的。
1:不要为了使用继承而继承。工人和学生都有共性的成员,不要为了节省代码,让工人继承学生。
/* 如何使用继承:验证是否有 is a 的关系 例如:学生是人, 小狗是动物 注意:不要为了使用某些功能而继承,java只支持单继承 */ class DK { void Ip4S() { System.out.println("好玩"); } } class BGir extends DK { } class Demo { public static void main(String[] args) { new BGir().Ip4S(); } } |
理解继承细节;
1:类名的设定,被继承的类称之为父类(基类),继承的类称之为子类
2:和传统的理解相反,子类并不是父类的一个子集。子类应当比父类包含更多的属性和行为。
3:父类的所有的私有成员不能继承
4:使用继承要符合is a关系。不要为了使用继承而继承
5:Java只支持单一继承,不支持多继承但是可以多重继承例如C继承B, B继承A
1.6 super关键字
子类继承它的父类中所有可以访问的成员变量和方法。子类不能够继承父类构造方法,但是父类的构造方法能从子类调用吗?学过了关键字this,都知道this关键字是一个当前对象引用通过this可以调用本类构造方法。如果当前对象有父类。如何在子类调用父类的构造方法?可以使用super关键字。那么super关键字的出现是指的是该类的父类。
super用途:
super关键字可以用于调用父类的构造方法。
super关键字可以调用父类的成员
1.6.1 子类访问父类的成员变量
案例:
1:定义Father(父类)类
1:成员变量int x=1;
2:构造方法无参的和有参的,有输出语句
2:定义Son类extends Father类
1:成员变量int y=1;
2:构造方法无参和有参的。有输出语句
1:this.y=y+x;
3:创建Son类对象
Son son=new Son(3);
System.out.println(son.y); //4
class Father { int x = 1; Father() { System.out.println("这是父类无参构造"); } Father(int x) { this.x = x; System.out.println("这是父类有参构造"); } void speak() { System.out.println("我是父亲"); } } class Son extends Father { int y = 1; Son() { System.out.println("这是子类的无参构造"); } Son(int y) { this.y = y + x; System.out.println("这是子类的有参构造"); } void run() { super.speak(); // 访问父类的函数 System.out.println("我是儿子"); } } class Demo6 { public static void main(String[] args) { Son s = new Son(3); System.out.println(s.y);// 4 } } |
4:子类对象为什么可以访问父类的成员。
1:this.y=y+x;有一个隐式的super super.x
5:super关键字作用
1:主要存在于子类方法中,用于指向子类对象中父类对象(不严谨)。
2:访问父类的属性
3:访问父类的函数
4:访问父类的构造函数
6:super注意
this和super很像,this指向的是当前对象的调用,super指向的是当前调用对象的父类。Demo类被加载,执行main方法,Son.class加载,发现有父类Father类,于是Father类也加载进内存。类加载完毕,创建对象,父类的构造方法会被调用(默认自动无参),然后执行子类相应构造创建了一个子类对象,该子类对象还包含了一个父类对象。该父类对象在子类对象内部。this super只能在有对象的前提下使用,不能在静态上下文使用。
1.6.2 super调用父类构造方法
1.创建一个子类对象时,子类构造方法会在开始执行时,先去调用它的父类的构造方法。
案例:验证该说法
提示:自定义父类(无参数构造),自定义子类(无参数构造),子类继承父类。
使用子类无参数构造创建对象。测试父类无参数构造是否执行。
2.子类默认调用父类无参数构造
如果在子类的构造函数中没有显示的调用父类的构造方法,编译器会自动的在子类构造函数第一行使用super调用父类的无参数构造。
案例:验证该说法。
提示:自定义父类(父类自定义有参数构造),自定义子类,子类继承父类。
总结:子类的构造函数默认第一行会默认调用父类无参的构造函数,隐式语句super();
如果父类无参构造函数不存在,编译报错。
3.子类显式调用父类构造函数
在子类构造函数第一行通过super关键字调用父类任何构造函数。如果显式调用父类构造函数,编译器自动添加的调用父类无参数的构造就消失。构造函数间的调用只能放在第一行,只能调用一次。super() 和this()不能同时存在构造函数第一行。
案例:验证上述说法
1.6.3 super调用父类的方法
关键字不仅仅可以引用父类的构造方法,也可以引用父类的方法。格式如下;
super.父类方法名();
class Father { void run(){ System.out.println("father"); } } class Son extends Father { public Son() { } void sleep(){ super.run(); } } |
本案例中super是没有必要的,因为子类继承了父类。那么什么时候才需要使用super呢?在即将学到的重写中就可以看到。
4:super思考
如果开发者自定义了一个类,没有显示的进行类的继承,那么该类中成员函数是否可以使用super关健健字?可以使用,继承了Object类,Object类是所有类的父类。
class Demo7 { public void print(){ System.out.println(super.toString()); } public static void main(String[] args){ new Demo7().print(); System.out.println(); } } |
1.7 重写(Override)
当子类继承父类,也就继承了父类的方法,有时候,子类需要修改父类中的方法。Java支持这种做法。叫做覆盖(overriding)
案例:测试覆盖
提示:父类和子类中定义同名的函数。
1:定义Father类
1:姓名,吃饭方法,吃窝窝头。
2:定义Son类,继承Father
1:Son类中不定义任何成员,子类创建对象,仍然可以调用吃饭的方法。
2:父类的吃饭的方法,Son不愿吃。Son自己定义了吃饭的方法。
1:此时父类中有一个吃饭的方法,子类中有2个吃饭的方法,一模一样,只是方法体不一样。
2:一个类中两个函数一模一样,是不允许的。
1:编译运行,执行了子类的方法。
2:使用父类的方法,在子类方法中,使用super.父类方法名。
class Father { String name; void eat() { System.out.println("吃窝窝"); } } class Son extends Father { public void eat() { // 继承可以使得子类增强父类的方法 System.out.println("来俩小菜"); System.out.println("来两杯"); System.out.println("吃香喝辣"); System.out.println("来一根"); } } class Demo8 { public static void main(String[] args) { Son s = new Son(); //执行子类的方法 s.eat(); } } |
3:该现象就叫做重写(覆盖 override)
1: 在继承中,子类可以定义和父类相同的名称且参数列表一致的函数,将这种函数
称之为函数的重写.
4:前提
1:必须要有继承关系
5:特点
1:当子类重写了父类的函数,那么子类的对象如果调用该函数,一定调用的是重写过后的函数。
可以通过super关键字进行父类的重写函数的调用。
2: 继承可以使得子类增强父类的方法
6:细节
1: 函数名必须相同
2:参数列表必须相同
3: 子类重写父类的函数的时候,函数的访问权限必须大于等于父类的函数的访
问权限否则编译报错
4:子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类。不能返回比父类更大的数据类型: 如子类函数返回值类型是Object
1:定义 A B C 类 B extends A
2:Father类中定义A getA();
3:Son 类中重写getA(); 方法,尝试将返回值修改为B,C ,Object
1:B编译通过
2:C 编译失败 ,没有继承关系
3:Object编译失败,比父类的返回值类型更大
class A { } class B extends A { } class C { } class Father { String name; void eat() { System.out.println("吃窝窝"); } // 定义一个函数,获取A类的对象, A getA() { return new A(); } } class Son extends Father { public void eat() { // 继承可以使得子类增强父类的方法 System.out.println("来两杯"); System.out.println("来俩小菜"); super.eat(); System.out.println("来一根"); } // B类是A类的子类 B getA() { return new B(); } } class Demo8 { public static void main(String[] args) { Son s = new Son(); s.eat(); } } |
7:子类对象查找属性或方法时的顺序:
1:原则:就近原则。
如果子类的对象调用方法,默认先使用this进行查找,如果当前对象没有找到属性或方法,找当前对象中维护的super关键字指向的对象,如果还没有找到编译报错,找到直接调用。
8:重载和重写的不同
1:重载(overload):
1:前提: 所有的重载函数必须在同一个类中
2:特点:
函数名相同,参数列表不同,与其他的无关(访问控制符、返回值类型)
3:不同:
个数不同 、 顺序不同、 类型不同
2:重写(override):
1:前提: 继承
2:特点:
函数名必须相同、参数列表必须相同。
子类的返回值类型要等于或者小于父类的返回值
9:重写练习
描述不同的动物不同的叫法
1:定义动物类
有名字,有吃和叫的方法
2:定义狗继承动物重写父类吃和叫的方法
3:定义猫继承动物重写父类吃和叫的方法
class Animal{ int x=1; String name; void eat(){ System.out.println("吃东西"); } void shout(){ System.out.println("我是动物"); } } class Dog extends Animal{ void eat(){ System.out.println("啃骨头"); } void shout(){ System.out.println("旺旺"); } void eat(String food){ System.out.println("吃:"+food); } } class Cat extends Animal{ void eat(){ System.out.println("吃老鼠"); } void shout(){ System.out.println("喵喵"); } } class Demo9{ public static void main(String[] args){ Dog d=new Dog(); d.shout(); d.eat();
Cat c=new Cat(); c.shout(); c.eat(); System.out.println(); } } |
1.8 instanceof 关键字
1:快速演示instanceof
Person p=new Person(); System.out.println( p instanceof Person); |
2:instanceof是什么?
1:属于比较运算符:
2:instanceof关键字:该关键字用来判断一个对象是否是指定类的对象。
3:用法:
对象 instanceof 类;
该表达式是一个比较运算符,返回的结果是boolea类型 true|false
3:案例
定义一个功能表函数,根据传递进来的对象的做不同的事情,如果是狗让其看家,如果是猫让其抓老鼠
1:定义动物类
2:定义狗类继承动物类
3:定义猫类继承动物类
4:定义功能根据传入的动物,执行具体的功能
5:instanceof好处
1:可以判断对象是否是某一个类的实例
package oop01; /* instanceof 比较运算符 检查是否是类的对象 1:可以判断对象是否是某一个类的实例 用法 对象 instanceof 类; 案例 定义一个功能函数,根据传递进来的对象的做不同的事情 如果是狗让其看家,如果是猫让其抓老鼠 1:定义动物类 2:定义狗类继承动物类 3:定义猫类继承动物类 4:定义功能根据传入的动物,执行具体的功能 */ class Animal { String name; void eat() { System.out.println("吃东西"); } void shout() { System.out.println("我是动物"); } } class Dog extends Animal { void eat() { System.out.println("啃骨头"); } void shout() { System.out.println("旺旺"); } } class Cat extends Animal { void eat() { System.out.println("吃老鼠"); } void shout() { System.out.println("喵喵"); } } class Demo11 { public static void main(String[] args) { Demo11 d = new Demo11(); // 对象 instanceof 类; System.out.println(d instanceof Demo11); d.doSomething(new Dog()); d.doSomething(new Cat()); } // 定义一个功能函数,根据传递进来的对象的做不同的事情 // 如果是狗让其看家,如果是猫让其抓老鼠 // 对象 instanceof 类; void doSomething(Animal a) { if (a instanceof Dog) { a.eat(); a.shout(); System.out.println("小狗看家"); } else if (a instanceof Cat) { a.eat(); a.shout(); System.out.println("抓老鼠"); } } } |
练习:
byte[] bs = new byte[] { 1, 2, 3 }; int[] is = new int[] { 1, 2, 3 }; String[] ss = new String[] { "jack", "lucy", "lili" }; System.out.println(bs instanceof byte[]); // true System.out.println(is instanceof int[]); // true System.out.println(ss instanceof String[]); // true // System.out.println(bs instanceof int[]); // 不可转换的类型 |
1.9 final关键字
常量:
1:定义静态方法求圆的面积
2:定义静态方法求圆的周长
3:发现方法中有重复的代码,就是PI,圆周率。
1:如果需要提高计算精度,就需要修改每个方法中圆周率。
4:描述一个变量
1:方法都是静态的,静态只能访问静态,所以变量也定义为静态的。
public static double PI=3.14;
1:如果定义为public后,新的问题,类名.PI=300; 改变了PI的值。
2:修改为private,修改为private后进行了封装,需要getset公共访问方法。
3:现有的知识不能解决这样的问题。可以使用final
class Demo12 { public static final double PI = 3.14; // 静态常量 public static double getArea(double r) { return PI * r * r; } public static double getLength(double r) { return PI * r * 2; } public static void main(String[] args) { // Demo12.PI=300; 无法为最终变量 PI 指定值 System.out.println(Demo12.PI); } } |
使用final
1:final关键字主要用于修饰类、类成员、方法、以及方法的形参。
final修饰成员属性:
1:说明该成员属性是常量,不能被修改。
public static final double PI=3.14;
1:public :访问权限最大
2:static :内存中只有一份
3:final :是一个常量
4:常量名大写
5:必须初赋值。
2:使用类名.成员。修改该成员的值,报错。--常量不能被修改
1:基本数据类型,final使值不变
2:对象引用,final使其引用恒定不变,无法让其指向一个新的对象,但是对象自身却可以被修改。
3:该关键字一般和static关键字结合使用
1:常量可以优先加载,不必等到创建对象的时候再初始化。
4:final和static可以互换位置
final修饰类:
1:该类是最终类,不能被继承。
1:将父类加final修饰,子类继承,就会报错。
2:查看api文档发现String类是final的。Integer类也是final的
1:为了防止代码功能被重写
2:该类没有必要进行扩展final修饰方法
final修饰方法
1:该方法是最终方法,不能被重写
2:当一个类被继承,那么所有的非私有函数都将被继承,如果函数不想被子类继承并重写可以将该函数final修饰
3:当一个类中的函数都被修饰为final时,可以将类定义为final的。
class Father2{ final void eat(){ System.out.println("eating...."); } } class Son2 extends Father2{ //该方法是最终方法,不能被重写 void eat(){ System.out.println("eating...."); } } class Demo12 { public static void main(String[] args) { // Demo12.PI=300; 无法为最终变量 PI 指定值 System.out.println(Demo12.PI); Son2 s=new Son2(); s.eat(); } |
final关键字修饰形参
1:当形参被修饰为final,那么该形参所属的方法中不能被篡改。
2: 项目中主要用于一些只用来遍历未知数据的函数。将未知变量声明为final的。增强数据的安全性。
class Demo14 { public static void main(String[] args) { System.out.println(); String[] arr = { "think in java", "java就业教程", "java核心技术" }; print(arr); } // 该方法,打印书名。 public static void print(final String[] arr) { //arr = null; ,无法重新赋值 for (int x = 0; x < arr.length; x++) { System.out.println(arr[x]); } } } |
思考
为什么子类一定要访问父类的构造函数呢
1:子类继承了父类的属性,如果要使用父类的属性必须初始化,创建子类对象,必须先初始化父类属性
必须调用父类的构造方法。
2:为什么调用父类无参的构造函数
设计java语言之时,只知道编译器会默认添加无参的构造函数,有参的无法确定。
但是可以通过super关键字显式调用父类指定构造函数。
3:为什么super()this()语句要放在构造函数的第一行
子类可能会用到父类的属性,所以必须先初始化父类。
2、多态
2.1 抽象类
为什么使用抽象类
1:定义Dog类
有颜色属性和叫的方法
2:定义Bird类
有颜色属性和叫的方法
3:定义其父类Animal
1:抽取共性颜色属性和叫的方法
1:颜色的属性可以使用默认初始化值。
2:叫的方法在父类中如何定义?
1:狗是旺旺
2:鸟是叽叽喳喳
3:可以将父类的方法定义为狗叫让鸟继承父类重写叫的方法
1:鸟怎么确定是否要重写父类方法。
2:不重写,编译和运行都没有问题,只是执行鸟叫的方法就会出现狗叫
4:父类的方法很难确定。
class Animal { String color; void shout(){ //如何定义呢?是旺旺还是叽叽喳喳? } } class Dog extends Animal { void shout() { System.out.println("旺旺"); } } class Bird extends Animal { void shout() { System.out.println("叽叽喳喳"); } } |
2:使用abstract
4:抽象类
1:当描述一个类的时候,如果不能确定功能函数如何定义,那么该类就可以定义为抽象类,功能函数应该描述为抽象函数。
5:抽象类的实现方式
1:定义animal类
1:定义叫的方法,无法确定方法体,不写方法体
1:public void shout (); 编译失败
2:根据提示在shout的方法加入abstract修饰
1:编译失败,有新的提示
3:根据提示将类加入abstract修饰
1:编译通过
abstract class Animal { String color; abstract void shout(); } class Dog extends Animal { void shout() { System.out.println("旺旺"); } } class Bird extends Animal { void shout() { System.out.println("叽叽喳喳"); } } |
6:抽象类的特点
1:有抽象函数的类,该类一定是抽象类。
2:抽象类中不一定要有抽象函数。
3:抽象类不能使用new创建对象
1:创建对象就是为了使用对象的功能,但是抽象类的方法,没有方法体。调用抽象方法没有意义.
4:抽象类主要为了提高代码的复用性,让子类继承来使用。
5:编译器强制子类实现抽象类父类的未实现的方法。
1:可以不实现,前提是子类的也要声明为抽象的。
7:抽象的优点
1:提高代码复用性
2:强制子类实现父类中没有实现的功能
3:提高代码的扩展性,便于后期的代码维护
例如:
8:抽象类不能创建对象,那么抽象类中是否有构造函数?
1:抽象类中一定有构造函数。主要为了初始化抽象类中的属性。通常由子类构造函数中通过super来访问。
9:final和abstract是否可以同时修饰一个类?
一定不能同时修饰。
abstract class Animal { String name; // 抽象类可以有构造函数 Animal() { } Animal(String name) { this.name = name; } abstract void shout(); } class Dog extends Animal { Dog() { } Dog(String name) { super(name); } void shout() { System.out.println("旺旺"); } } class Demo3 { public static void main(String[] args) { // 抽象类不能创建对象 // Animal a=new Animal(); Dog d = new Dog("旺财"); System.out.println(); } } |
2:抽象练习
1:定义抽象类MyShape(图形)
1:定义抽象方法获取图形的长度和面积
2:定义子类Rect继承父类MyShape
1:定义自身特有的长和宽(成员变量) width height;
2:实现父类未实现的函数。
3:定义子类 Circle实现父类MyShape
1:定义自身特有的半径和圆周率(使用常量)
2:实现父类为实现的方法。
/* } 2:抽象练习 1:定义抽象类MyShape(图形) 1:定义抽象方法获取图形的长度和面积 2:定义子类Rect继承父类MyShape 1:定义自身特有的长和宽(成员变量) width height; 2:实现父类未实现的函数。 3:定义子类 Circle实现父类MyShape 1:定义自身特有的半径和圆周率(使用常量) 2:实现父类为实现的方法。 */ abstract class MyShape { abstract double getLen(); abstract double getArea(); } class Rect extends MyShape { double width; double height; Rect() { } Rect(double width, double height) { this.width = width; this.height = height; } double getLen() { return 2 * (width + height); } double getArea() { return width * height; } } class Circle extends MyShape { double r; public static final double PI = 3.14; Circle() { } Circle(double r) { this.r = r; } double getLen() { return 2 * PI * r; } double getArea() { return PI * r * r; } } class Demo4 { public static void main(String[] args) { Rect r = new Rect(5, 5); System.out.println(r.getLen()); System.out.println(r.getArea()); System.out.println(); Circle c = new Circle(5); System.out.println(c.getLen()); System.out.println(c.getArea()); } } |
2.2 java数值交换
1:基本数据类型交换
1:定义changeBase方法,定义形参,接收2个int变量,定义第三方变量交换。
2:main方法定义2个int类型变量,调用changeBase方法进行交换。
3:分析内存
1:main方法的变量在栈内存
2:main方法调用changeBase方法,方法中的变量也在栈内存
3:changeBase方法运行完释放。
4:main方法中的2个变量没有交换。
2:数组数据类型值交换
1:定义changeArr方法,接收int数组,接收指定角标互换。
2:内存分析
1:main方法进栈,定义数组类型变量,同时堆内存开辟空间,数组变量持有该引用。
2:main方法加载changeArr方法。
3:changeArr方法释放,数组完成交换。
3:数组数据类型值可以交换
3:类类型进行值交换
1:定义changeClass方法,接收对象,接收成员变量新值
2:内存分析
1:main方法进栈,定义类类型变量,堆内存开辟空间,持有该引用。
2:main方法调用changeClass方法
3:changeClass方法运行完毕,释放。
4:实例变量的值改变。
3:对象所维护的实例变量的值改变。
4:总结
1:基本类型不能交换
2:所有引用类型可以交换(数组,类)但是String不可以
/* 数值交换 基本数据类型交换 数组数据类型值交换 类类型进行值交换 4:总结 1:基本类型不能交换 2:所有引用类型可以交换(数组,类)除了String */ import java.util.Arrays; class Demo5 { int x = 1; public static void main(String[] args) { int x = 1; int y = 3; System.out.println("调换前:" + x + ":" + y); changeBase(x, y); System.out.println("调换后:" + x + ":" + y); System.out.println("array....."); int[] arr = { 1, 2, 3, 4, 5 }; System.out.println("调换前" + Arrays.toString(arr)); changeArr(arr, x, y); System.out.println("调换后" + Arrays.toString(arr)); System.out.println("class...."); Demo5 d = new Demo5(); System.out.println("修改前:" + d.x); changeClass(d, 5); System.out.println("修改后:" + d.x); } // 1:定义changeBase方法,定义形参,接收2个int变量,定义第三方变量交换。 public static void changeBase(int x, int y) { int temp = x; x = y; y = temp; System.out.println("调换后:" + x + ":" + y); } // 2:定义changeArr方法,接收int数组,接收指定角标互换。 public static void changeArr(int[] arr, int x, int y) { int temp = arr[x]; arr[x] = arr[y]; arr[y] = temp; } // 类类型进行值交换 // 1:定义changeClass方法,接收对象,接收成员变量新值 public static void changeClass(Demo5 d, int y) { d.x = y; } } |
2.3 接口
1:没有接口会出现什么问题
1:定义铅笔类(Pencil),名字,写功能
2:定义带橡皮的铅笔(PencilWithEraser),写功能,擦除功能
1:带橡皮的铅笔is a 铅笔有继承关系,继承了铅笔
3:定义橡皮类(Eraser),擦除功能
1:带橡皮的铅笔有橡皮也可以继承橡皮,但java只支持单继承
4:由于java单继承的限制,无法解决现实中这类问题,那么就有了接口
5:发现带橡皮的铅笔是铅笔只是进行了功能加强,具备了擦除功能,可以把橡皮定义为接口
class Pencil { String name; Pencil() { } Pencil(String name) { this.name = name; } void write() { System.out.println("写字"); } } interface Eraser { public static final String color = "白色"; public abstract void clean(); } // 1:带橡皮的铅笔类继承铅笔类实现橡皮接口 class PencilWithEraser extends Pencil implements Eraser { PencilWithEraser() { } PencilWithEraser(String name) { super(name); } void write() { System.out.println(name + ":考试专用"); } public void clean() { System.out.println(super.name + ":带橡皮的铅笔,就是好用"); } } class Demo6 { public static void main(String[] args) { PencilWithEraser pe = new PencilWithEraser("中华2B"); pe.write(); pe.clean(); System.out.println(pe.color); System.out.println(PencilWithEraser.color); } } |
2:接口是什么
1:接口usb接口,usb接口主要用于扩展笔记本的功能。
1:笔记本硬盘空间不够,使用usb移动硬盘解决问题。
2:java中接口:扩展java定义的类的功能,解决了java单继承的缺陷。
3:接口定义
interface关键字,用于定义一个接口类。
interface 接口名{
}
1:描述接口的属性(成员变量)
2:描述接口的行为(成员方法)
4:使用接口
implements 关键字可以使子类实现指定的多个接口
5:接口特点
1:接口中定义的所有属性默认是public static final的,即静态常量
1:即使静态常量,那么定义的时候必须赋值。
2:接口中定义的方法不能有方法体,接口中定义的方法默认添加public abstract
1:即接口不能定义实现方法
2:不添加public abstract也可以,默认添加,但不便于阅读
3:接口不可以创建对象,因为接口有抽象方法。
1:new 接口编译报错。
4:接口要被子类实现,子类对接口中的抽象方法全部覆盖后子类才可以实例化,否则子类是一个抽象类。
5:接口可以被类多实现
interface A{ } interface B{ } interface C{ } class D implements A,B,C{ } |
6:如果实现类要访问接口中的成员,不能使用super关键字,因为两者没有显示的继承关系,可以使用接口名访问。
6:接口中是否有构造函数
1:一定没有
1:构造函数用于初始化非静态成员变量,接口中变量是静态常量。
7:接口练习
1:带橡皮的铅笔类继承铅笔类实现橡皮接口
8:接口使用细节
1:一个实现类可以同时实现多个接口
1:接口A,接口B ,实现类C可以同时实现A和B,例如implements A,B
2:接口之间可以进行多继承。
1:接口A,接口B,接口C ,接口C 可以同时继承A,和B,中间用逗号隔开。
2:接口与接口之间不能实现
3:定义接口A,B,C 类D实现 A,B ,C 创建D类对象,使用instanceof 比较A,B,C 全是true
1:如果一个实现类实现了很多接口,那么这个实现类的对象是这些接口的对象。
5:接口A,接口B 都定义了同名方法,实现类该如何实现。
1:只用实现一个方法即可。
1:接口中的同名方法,有重复代码,可以通过接口的继承来解决
9:抽象类是强迫子类重写父类方法,接口是增强实现类功能。
2.4 多态
1:什么是多态
一个对象的多种状态
(老师)(员工)(儿子)
教师 a =make;
员工 b= make;
2:多态体现
1:Father类
1:非静态成员变量x
2:静态成员变量y
3:非静态方法eat,方法体输出父类信息
4:静态方法speak();方法体输出父类信息
2:Son类
1:非静态成员变量x
2:静态成员变量y
3:非静态方法eat,方法体输出子类信息
4:静态方法speak();方法体输出子类信息
class Father { int x = 1; static int y = 2; void eat() { System.out.println("开吃"); } static void speak() { System.out.println("小头爸爸"); } } class Son extends Father { int x = 3; static int y = 4; void eat() { System.out.println("大头儿子很能吃"); } static void speak() { System.out.println("大头儿子。"); } } class Demo10 { public static void main(String[] args) { Father f = new Son(); // 父类引用指向了子类对象。 System.out.println(f.x); // 1 System.out.println(f.y); // 2 f.eat(); // 输出的是子类的。 f.speak(); // 输出的是父类 } } |
3:Son类继承父类
1:创建Father f=new Son();
1:这就是父类引用指向了子类对象。
2:问f.x=?(非静态)
3:问f.y=?(静态)
4:问f.eat()输出的是子类还是父类信息?(非静态)
5:问f.speak()输出的是子类还是父类信息?(静态)
4:总结
1:当父类和子类具有相同的非静态成员变量,那么在多态下访问的是父类的成员变量
2:当父类和子类具有相同的静态成员变量,那么在多态下访问的是父类的静态成员变量
所以:父类和子类有相同的成员变量,多态下访问的是父类的成员变量。
3:当父类和子类具有相同的非静态方法(就是子类重写父类方法),多态下访问的是子类的成员方法。
4:当父类和子类具有相同的静态方法(就是子类重写父类静态方法),多态下访问的是父类的静态方法。
2:多态体现
1:父类引用变量指向了子类的对象
2:父类引用也可以接受自己的子类对象
3:多态前提
1:类与类之间有关系,继承或者实现
4:多态弊端
1:提高扩展性,但是只能使用父类引用指向父类成员。
5:多态特点
非静态
1:编译时期,参考引用型变量所属的类是否有调用的方法,如果有编译通过。没有编译失败
2:运行时期,参考对象所属类中是否有调用的方法。
3:总之成员函数在多态调用时,编译看左边,运行看右边。
在多态中,成员变量的特点,无论编译和运行参考左边(引用型变量所属的类)。
在多态中,静态成员函数特点,无论编译和运行都参考左边
6:多态练习
1:多态可以作为形参,接受范围更广的对象,避免函数重载过度使用。
1:定义功能(函数),该函数可以输出任何图形的面积和周长.根据输出任何图形的面积和周长。
1:定义抽象类abstract MyShape
1:定义抽象方法public abstract double getArea();
2:定义抽象方法public abstract double getLen();
2:定义Rect类继承MyShape
1:定义长和宽成员变量,double width height;
2:无参构造,有参构造。
3:实现父类方法。
3:定义Cricle类继承MyShape
1:定义半径成员变量,和PI常量
2:无参构造,有参构造
3:实现父类方法。
4:定义静态方法计算任意图形的面积和周长
1:未知内容参与运算,不能确定用户传入何种图形,使用多态。
1:形参定义为 MyShape my
2:调用计算面积方法,和计算周长方法。并打印
2:使用多态特性,子类重写了父类非静态方法,会执行子类的方法。
/* 多态练习 1:多态可以作为形参,接受范围更广的对象,避免函数重载过度使用。 1:定义功能,根据输出任何图形的面积和周长。 子类重写了父类的抽象方法,多态下,会执行子类的非静态方法。 2:多态可以作为返回值类型。 获取任意一辆车对象 3:抽象类和接口都可以作为多态中的父类引用类型。 */ abstract class MyShape{ public abstract double getArea(); public abstract double getLen(); } class Rect extends MyShape{ double width ; double height; Rect(){ } Rect(double width ,double height){ this.width=width; this.height=height; } public double getArea(){ return width*height; } public double getLen(){ return 2*(width+height); } } class Circle extends MyShape{ double r; public static final double PI=3.14; Circle(){ }
Circle(double r){ this.r=r; } public double getLen(){ return 2*PI*r; }
public double getArea(){ return PI*r*r; } } class Demo11{ public static void main(String[] args){ System.out.println(); print(new Rect(3,4)); //MyShape m =new Rect(3,4); print(new Circle(3)); }
//根据用户传入的图形对象,计算出该图形的面积和周长 //1:多态可以作为形参,接受范围更广的对象,避免函数重载过度使用。 public static void print(MyShape m){ System.out.println(m.getLen()); System.out.println(m.getArea()); } } |
2:多态可以作为返回值类型。
定义一个汽车工厂类,获取任意一辆车对象,该工厂生产的汽车随机.
1:定义汽车类,有名字和颜色,提供有参和无参构造,有运行的行为。
2:定义Bmw类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。
3:定义Benz类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。
4:定义Bsj类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。
5:定义静态方法,汽车工厂,随机生产汽车。使用多态定义方法返回值类型。
1:使用(int)Math.round(Math.random()*2); 生成0-2之间随机数。
2:使用if else 判断,指定,0,1,2 new 不同汽车 并返回。
6:调用该方法,发现多态的好处。
* 2:多态可以作为返回值类型。 获取任意一辆车对象 1:定义汽车类,有名字和颜色,提供有参和无参构造,有运行的行为。 2:定义Bmw类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。 3:定义Benz类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。 4:定义Bsj类,继承Car类,提供无参构造和有参构造(super父类构造),重写父类运行行为。 5:定义静态方法,汽车工厂,随机生产汽车。使用多态定义方法返回值类型。 1:使用(int)Math.round(Math.random()*2); 生成0-2之间随机数。 Math 类 2:使用if else 判断,指定,0,1,2 new 不同汽车 并返回。 6:调用该方法,发现多态的好处。 */ class Car { String name; String color; Car() { } Car(String name, String color) { this.name = name; this.color = color; } void run() { System.out.println("跑跑。。。。"); } } class Bmw extends Car { Bmw() { } Bmw(String name, String color) { super(name, color); } void run() { System.out.println("宝马很拉风。。。。"); } } class Benz extends Car { Benz() { } Benz(String name, String color) { super(name, color); } void run() { System.out.println("奔驰商务首选。。。。"); } } class Bsj extends Car { Bsj() { } Bsj(String name, String color) { super(name, color); } void run() { System.out.println("泡妞首选。。。。"); } } class Demo12 { public static void main(String[] args) { int x = 0; while (x < 100) { Car c = CarFactory(); c.run(); x++; } } // 定义静态方法,汽车工厂,随机生产汽车。使用多态定义方法返回值类型。 // 使用随机数,0.1.2 if 0 bsj 1 bmw 2 bc public static Car CarFactory() { int x = (int) Math.round(Math.random() * 2); if (0 == x) { return new Bmw("宝马x6", "红色"); } else if (1 == x) { return new Benz("奔驰", "黑色"); } else if (2 == x) { return new Bsj("保时捷", "棕色"); } else { return new Benz("Smart", "红色"); } } } |
3:抽象类和接口都可以作为多态中的父类引用类型。
1:sun Arrays
6:多态之类型转型
1:案例定义Father类
1:定义method1和method2方法
2:定义Son类继承Father类
1:定义method1(重写父类method1)和method2方法,同时子类定义自身特有的method3方法.
3:创建Father f=new Son();
1: f.method1() 调用的子类或者父类?
2: f.method2() 编译和运行是否通过?
3: f.method3() 编译和运行是否通过?(编译报错)
4:如何在多态下,使用父类引用调用子类特有方法。
需要使用类类型装换.
1:基本类型转换:
1:自动:小->大
2:强制:大->小
2:类类型转换
前提:继承,或者实现.也就是必须有关系
1:自动:子类转父类
2:强转:父类转子类
3:类型转换
1:Son s=(Son)f
2:s.method3();
/* 如何在多态下,使用父类引用调用子类特有方法。 1:基本类型转换: 1:自动:小->大 int x=1 double d=x; 2:强制:大->小 int y=(int)d; 2:类类型转换 前提:继承,必须有关系 1:自动:子类转父类 Father f=new Son(); 2:强转:父类转子类 Son s=(Son)f; 1:类型转换 1:Son s=(Son)f 2:s.method3(); */ class Father { void method1() { System.out.println("这是父类1"); } void method2() { System.out.println("这是父类2"); } } class Son extends Father { void method1() { System.out.println("这是子类1"); } void method3() { System.out.println("这是子类3"); } } class Demo14 { public static void main(String[] args) { Father f = new Son(); f.method1(); // 这是子类1 f.method2(); // 这是父类2 // f.method3(); //编译报错。 // 多态弊端,只能使用父类引用指向父类成员。 // 类类型转换 Son s = (Son) f; s.method3(); System.out.println(); } } |
5:案例:测试类类型转换
本案例建立动物的继承体系.例如有父类Animal,子类Dog Fish等.每个子类有自己特有的行为.
请分别尝试,将Animal强制类型转换为Dog.
Dog强制类型转换为Fish,并观察发生了什么
1:定义Animal类颜色成员变量,无参构造,有参构造,run方法
2:定义Dog类,继承Animal,定义无参构造,有参构造(使用super调用父类有参构造),Dog的特有方法ProtectHome
3:定义Fish类,继承Animal,定义无参构造,有参构造(使用super调用父类有参构造),Fish特有方法swim
4:定义Bird类,继承Animal,定义无参构造,有参构造(使用super调用父类有参构造),Bird特有方法fly
5:使用多态,Animal a=new Dog();
6:调用Dog的特有方法,ProtectHome
1:类类型转换,Dog d=(Dog)a;
2:d.protectHome
7:非多态
1:Animal a=new Animal();
2:类类型转换
Dog d=(Dog)a;
d.protectHome();
3:编译通过,运行出现异常
1:ClassCastException
8:多态例外
1:Animal a=new Dog();
2:类类型转换
1:Fish f=(Fish)a;
2:f.fish();
3:编译通过,运行异常
1:ClassCastException
总结: 虽然是存在类与类之间继承关系,但是只有子类和父类之间有关系.但是鸟不能转为狗,狗不能转为鱼,他们之间没有关系。
class Animal { String color; Animal() { } Animal(String color) { this.color = color; } void run() { System.out.println("跑跑"); } } class Dog extends Animal { Dog() { } Dog(String color) { super(color); } void run() { System.out.println("狗儿跑跑"); } void protectHome() { System.out.println("旺旺,看家"); } } class Fish extends Animal { Fish() { } Fish(String color) { super(color); } void run() { System.out.println("鱼儿水中游"); } void swim() { System.out.println("鱼儿游泳"); } } class Demo15 { public static void main(String[] args) { Animal ani = new Dog(); // ani.protectHome(); // 正常转换 Dog d = (Dog) ani; d.protectHome(); // 多态例外 Animal an = new Animal(); // ClassCastException // Dog d=(Dog)an // 多态例外 Animal dog = new Dog(); // ClassCastException // Fish f = (Fish) dog; } } |
6:案例2
1:定义一功能,接收用户传入动物,根据用于传入的具体动物,执行该动物特有的方法
2:使用多态,方法形参,不能确定用户传入的是那种动物
3:使用instanceof 关键字,判断具体是何种动物,
4:类转换,执行该动物的特有方法。
package oop04; /* 案例2 1:定义一功能,接收用户传入动物,根据用于传入的具体动物,执行该动物特有的方法 2:使用多态,方法形参,不能确定用户传入的是那种动物 3:使用instanceof 关键字,判断具体是何种动物, 4:类转换,执行该动物的特有方法。 */ class Animal { String color; Animal() { } Animal(String color) { this.color = color; } void run() { System.out.println("跑跑"); } } class Dog extends Animal { Dog() { } Dog(String color) { super(color); } void run() { System.out.println("狗儿跑跑"); } void protectHome() { System.out.println("旺旺,看家"); } } class Fish extends Animal { Fish() { } Fish(String color) { super(color); } void run() { System.out.println("鱼儿水中游"); } void swim() { System.out.println("鱼儿游泳"); } } class Bird extends Animal { Bird() { } Bird(String color) { super(color); } void run() { System.out.println("鸟儿空中飞"); } void fly() { System.out.println("我是一只小小鸟。。。。"); } } class Demo16 { public static void main(String[] args) { System.out.println(); doSomething(new Dog()); doSomething(new Bird()); doSomething(new Fish()); } // 定义一功能,接收用户传入动物,根据用于传入的具体动物,执行该动物特有的方法 public static void doSomething(Animal a) { if (a instanceof Dog) { Dog d = (Dog) a; d.protectHome(); } else if (a instanceof Fish) { Fish f = (Fish) a; f.swim(); } else if (a instanceof Bird) { Bird b = (Bird) a; b.fly(); } else { System.out.println("over"); } } } |
2.5 内部类
inner class nested class
定义在类中的类
快速体验:
Outer类中有Inner类 Inner类就是内部类
class Outer { private int x = 1; // 成员内部类 private class Inner { int y = 2; int x = 3; void innerPrint() { System.out.println("内部类" + y); // 2 System.out.println(this.x); // 3 } } void outPrint() { System.out.println("外部类" + x); // 外部类的成员方法上,创建成员内部类对象。 Inner n = new Inner(); n.innerPrint(); } } class Demo1 { public static void main(String[] args) { Outer out = new Outer(); out.outPrint(); } } |
内部类分类
成员内部类,静态内部类(嵌套类),匿名内部类
2.5.1 成员内部类
1:成员内部类和成员变量和成员方法是并列的。
2:成员内部类也是类,也可以定义成员变量和成员方法
内部类和外部类的相互访问
在外部类的成员函数中创建内部类的对象,直接访问内部类的方法
案例
1:创建Outer类,是外部类,定义成员变量(例如 int x)和成员方法(例如:print方法)。
1:在Outer中,定义名字为Inner的内部类,定义成员变量(int y)和成员方法(例如innerPrint)。
2:创建内部类对象
3:外部类中调用内部类的成员方法
1:Outer类的print方法,在该方法中创建Inner对象,并调用Inner的方法innerPrint
4:内部类可以访问外部类的成员
1:外部类成员变量,在内部类中可以直接访问
5:内部类可以直接访问外部类的成员。
6:外部类和内部类定义了同名的成员变量(例如 int x=1;)
1:在内部类中,先找内部类中,没有找到再找外部类的。
2:如果内部类一定要访问外部类的成员
1:使用this 是Inner类的对象
2:使用this.Outer可以访问到外部类的成员变量。
其他类中直接创建内部类对象
Outer.Inner in=new Outer().new Inner();
看到这里其实就应该明白,内部类对象持有有一个所在外部类对象引用.所以在创建内部类对象时,要先创建外部类对象.
class Outer { private int x = 1; // 成员内部类 class Inner { int y = 2; int x = 3; void innerPrint() { System.out.println("内部类" + y); // 2 System.out.println(this.x); // 3 System.out.println(Outer.this.x); } } void outPrint() { System.out.println("外部类" + x); // 外部类的成员方法上,创建成员内部类对象。 Inner n = new Inner(); n.innerPrint(); } } class Demo1 { public static void main(String[] args) { Outer out = new Outer(); out.outPrint(); // 成员内部类创建方式 Outer.Inner in = new Outer().new Inner(); in.innerPrint(); } } |
2:为什么定义内部类
1:成员内部类作为外部类的成员,可以直接访问外部类的所有成员。
3:成员内部类被private修饰
1:外部类的成员变量和成员方法都可以被private修饰,成员内部类是否可以。
2:案例
1:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())
2:内部类Inner,定义成员变量和成员方法(例如int x,interPrint())
3:Inner被private修饰
4:在Outer 类的outerPrint()方法中创建Inner 对象,调用Inner 的interPrint()方法
1:编译通过,运行没有问题。
5:在其他类中创建内部类Inner对象
1:编译失败
3:被private修饰的成员内部类只能在内部类所在的外部类中创建和访问。
class Outer { private int x = 1; // 成员内部类 private class Inner { int y = 2; int x = 3; void innerPrint() { System.out.println("内部类" + y); // 2 System.out.println(this.x); // 3 System.out.println(Outer.this.x); } } void outPrint() { System.out.println("外部类" + x); // 外部类的成员方法上,创建成员内部类对象。 Inner n = new Inner(); n.innerPrint(); } } class Demo1 { public static void main(String[] args) { Outer out = new Outer(); out.outPrint(); // 被private修饰的成员内部类只能在内部类所在的外部类中创建和访问。 // Outer.Inner in = new Outer().new Inner(); // in.innerPrint(); } } |
2.5.2 嵌套类(静态内部类)
成员内部类被static修饰
1:案例
1:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())
2:内部类Inner,定义成员变量和成员方法(例如int x,interPrint())
3:内部类的成员变量使用static修饰
1:编译失败,提示内部类不能有静态声明
4:将内部类Inner使用static修饰
5:在Outer 类的outerPrint()方法中创建Inner 对象,调用Inner 的interPrint()方法
1:编译通过,运行没有问题。
6:在其他类中创建静态内部类Inner对象
1:编译提示,限定的新静态类。
2:Outer.Inner in=new Outer.Inner();
2:如果成员内部类包含静态成员,java规定内部类必须声明为静态的。
3:访问形式
1:Outer.Inner in=new Outer.Inner();
注意: 很显然创建静态内部类对象不需要外部类的对象.但是静态内部类不可以访问外部类的非静态成员.(静态不能访问非静态)可以将静态类理解为所在外部类的成员.可以直接通过类名访问静态类.
/* 成员内部类static修饰 如果成员内部类包含静态成员,java规定内部类必须声明为静态的。 访问形式 1:Outer.Inner in=new Outer.Inner(); */ class Outer { int x = 2; static int y = 1; // 成员内部类被static修饰 static class Inner { int y = 2; int x = 3; static int z = 5; // 内部类有静态成员,类也需要是static void innerPrint() { System.out.println("内部类" + y); // 2 System.out.println(this.x); // 3 System.out.println(z); } } void outPrint() { System.out.println("外部类" + x); Inner n = new Inner(); n.innerPrint(); } } class Demo2 { public static void main(String[] args) { Outer out = new Outer(); // 静态内部类创建方式 Outer.Inner in = new Outer.Inner(); in.innerPrint(); } } |
2.5.3 局部内部类
1:案例定义局部内部类
2:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())
3:在outerPrint()方法中定义一个类(例如LocalHost),该类有成员变量(例如int y),有方法interPrint();
4:LocalHost类就叫做局部内部类,和局部变量int x是并列的。
5:创建局部内部类对象(LocalHost类的对象)
1:在outerPrint()方法中创建LocalHost类的对象并调用interPrint方法。
2:编译通过,正常运行。
6:局部内部类访问所在方法的局部变量x
1:在outerPrint()方法中创建LocalHost类的对象并调用interPrint方法。
2:局部内部类(LocalHost类)的对象调用interPrint方法访问outerPrint方法中的局部变量x
1:编译报错内部类访问局部变量,需要声明为最终类型。
3:局部内部类访问所在方法的局部变量,该变量需要是final的。
7:为什么加final
1:生命周期
注意:方法的局部变量位于栈上,只存在于该方法的声明周期内,随着方法的运行而存在,方法的消失而消失.当方法结束时,栈内存会释放,变量消失.但是即使方法运行完毕,在该方法中创建的内部类对象仍然可能存活于堆内存中(例如将局部内部类的引用传递到其他代码中).由于生命周期的不同,所以内部类对象不能使用它们,如果一定要使用就需要将局部变量声明为final的.
注意: 局部内部类对象只能在该类所在的方法中实例化.
class Outer { int x = 2; static int y = 1; void outPrint() { // 局部内部类访问局部变量需要使用final修饰 final int y = 2; class LocalClass { int x = 3; void innerPrint() { System.out.println("局部内部类" + x); System.out.println(y); } } System.out.println("外部类" + x); LocalClass lc = new LocalClass(); lc.innerPrint(); } } class Demo3 { public static void main(String[] args) { Outer out = new Outer(); out.outPrint(); System.out.println(); } } |
2.5.4 匿名内部类
案例
2:定义接口MyInterface,成员变量(静态常量必须初始化)和方法print(默认public abstract)。
2:外部类Outer,定义成员变量成员方法(例如int x, outerPrint())
3:内部类Inner 实现MyInterface接口,实现print方法。
4:创建内部类对象
在outerPrint方法中创建内部类对象,调用内部类的方法。
5:此种案例的代码可以使用匿名内部类来实现。
6:修改代码,使用匿名内部类
1:在outerPrint方法中 new MyInterface(){}; 花括号中需要实现接口的print方法
2:接口不能new。
1:这里不是new接口,是在创建一个接口的实现类,只不过该接口的实现类没有名字。
3:new MyInterface(){}; 其实就是内部类的一种替换格式。
1:调用print方法new MyInterface(){}.print();
2:匿名内部类是内部类的简写格式
3:使用匿名内部类的前提
1:内部类要继承一个类或者实现接口
4:格式 new 父类或接口(){子类内容}
5:匿名内部类就是一个匿名子类对象
6:如果只调用一个方法可以new MyInterface(){}.print(); 形式调用,如果接口或者父类中有2个以上的方法,就需要起名字了。
1:MyInterface in=new MyInterface(){};
2:in.print();
3:多态的体现。
interface MyInterface { public static final int x = 3; public abstract void print(); public abstract void run(); } class Outer { int x = 2; static int y = 1; void outPrint() { final int y = 2; class LocalClass implements MyInterface { public void print() { System.out.println("局部内部类。。。。"); } public void run() { System.out.println("跑跑。。。"); } } LocalClass lc = new LocalClass(); lc.print(); } } class Demo4 { public static void main(String[] args) { new Outer().outPrint(); System.out.println(); } } |
interface MyInterface { public static final int x = 3; public abstract void print(); } class Outer { int x = 2; static int y = 1; void outPrint() { final int y = 2; // class LocalClass implements MyInterface { // public void print() { // System.out.println("局部内部类。。。。"); // } // } // } // // LocalClass lc = new LocalClass(); // lc.print(); //是对上述注释的简写 new MyInterface() { public void print() { System.out.println("匿名内部类。。。。"); } }.print(); } } class Demo4 { public static void main(String[] args) { new Outer().outPrint(); System.out.println(); } } |