-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
一、继承
(一)继承概述
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名 {}
单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
(二)继承的好处与弊端
1. 继承的好处:
(1)提高了代码的复用性
多个类相同的成员可以放到同一个类中
(2)提高了代码的维护性
如果功能的代码需要修改,修改一处即可
(3)让类与类之间产生了关系,是多态的前提
其实这也是继承的一个弊端:类的耦合性很强
2. 继承的弊端
(1)耦合性增强,违背了我们的开发原则(高内聚,低耦合)
(2)高内聚:能自己干的事儿从来不求别人
(3)低耦合:类与类直接不要产生太多依赖
(三)Java中继承的特点
1. 一个类只能有一个父类,不可以有多个父类。
理解:一个儿子只有一个亲爹
举例:
class SubDemo extends Demo{} //true
class SubDemo extends Demo1,Demo2...//error
2. Java支持多层继承(继承体系)
理解:类似家谱
举例:
class A{}
class B extends A{}
class C extends B{}
(四)注意事项
1. 子类只能继承父类所有非私有的成员(成员方法和成员变量),父类的私有成员,子类不能继承,其实这也体现了继承的另一个弊端:打破了封装性
2. 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
3. 不要为了部分功能而去继承
4. 我们到底在什么时候使用继承呢?
继承中类之间体现的是:”is a”的关系。
(五)super关键字
1. super的用法和this很像
this代表本类对应的引用。
super代表父类存储空间的标识(可以理解为父类引用)
2. 用法(this和super均可如下使用)
访问成员变量
this.成员变量 super.成员变量
访问构造方法(子父类的构造方法问题讲)
this(…) super(…)
访问成员方法(子父类的成员方法问题讲)
this.成员方法() super.成员方法()
案例演示:
class PersonTest{
public static void main(String[] args){
Student s = new Student("子明", 18,"czbk001");
System.out.println(s.name +"--" + s.age +"--"+ s.studyNum);
s.eat(); // 继承来的
s.sleep(); //继承来的
s.study();
Teacher t = new Teacher("凌子峰", 48 ,"czbk008");
System.out.println(t.name +"--" + t.age +"--"+ t.workNum);
t.eat();
t.sleep();
t.work();
}
}
class Person{
String name ;//姓名
int age; //年龄
int money;
public void eat(){
System.out.println("下雨天 ,吃点热乎的 ");
}
public void sleep(){
System.out.println("下雨天 ,特别适合补一觉,但是我还有事需要做。。。");
}
}
class Student extends Person{
String studyNum; //学号
Student(){}
Student(String name , int age, String studyNum){
this.name = name;
this.age = age;
this.studyNum = studyNum;
}
public void study(){
System.out.println("下雨天 , 好好学习");
}
}
class Teacher extends Person{
String workNum; //工号
Teacher(){}
Teacher(String name , int age, String workNum){
this.name = name;
this.age = age;
this.workNum = workNum;
}
public void work(){
System.out.println("下雨天 , 好好讲课");
}
}
运行结果:
(六)继承中成员变量的关系
成员变量的关系
不同名字:该用谁的就用谁的
相同名字:
1.子类使用的是子类自己的
2.子类想使用父类的变量,使用super关键字
理解: super代表了父类引用
this:你创建了当前对象,this 是当前对象的引用
super:你没有创建父类对象,super可以理解为父类的引用,真正代表的是,父类存储空间的标识。
案列演示:
class VarDemo{
public static void main(String[] args){
//创建子类对象
Zi z = new Zi();
z.show2();
System.out.println();
}
}
class Fu{
int num1 =10;
public void show(){
System.out.println(num1);
}
}
class Zi extends Fu{
int num1=20;
public void show2(){
int num1=30;
System.out.println("num1="+num1);
System.out.println("num1="+this.num1);
System.out.println("num1="+super.num1);
}
}
运行结果:
结论:在子类方法中访问一个变量的过程
1. 首先在子类局部范围找
2. 然后在子类成员范围找
3. 最后在父类成员范围找(肯定不能访问到父类局部范围)
4. 如果还是没有就报错。(不考虑父亲的父亲…)
(七)继承中构造方法的关系
明确:子类不能继承父类的构造方法,但是我们可以调用父类的构造方法
1. 子类中所有的构造方法默认都会访问父类中空参数的构造方法
2. 为什么呢?
因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。
3. 每一个构造方法的第一条语句默认都是:super()
4. 如果父类中没有构造方法,该怎么办呢?
子类通过super去显示调用父类其他的带参的构造方法
子类通过this去调用本类的其他构造方法
本类其他构造也必须首先访问了父类构造
一定要注意:
super(…)或者this(….)必须出现在第一条语句上,并且只能有一个。
否则,就会有父类数据的多次初始化
演示案列:
class Test {
public static void main(String[] args) {
Zi z = new Zi(20);
z.show();
}
}
class Fu{
public int num = 10;
private int num2;
public Fu(int num2 ){
this.num2 = num2;
}
public void show(){
System.out.println("num2="+ num2);
}
}
class Zi extends Fu{
public int num;
public Zi(){
this(0);
System.out.println("zi");
}
public Zi(int num){
super(num);
this.num = num;
System.out.println("zi");
}
public void show(){
int num = 30;
System.out.println("num="+num);
System.out.println("num="+this.num);
System.out.println("num="+super.num);
super.show();
}
}
运行结果:
(八)继承中成员方法的关系
1. 成员方法的关系:
当方法名不同时,子类可以直接调用父类的方法
相同名字:
(1)子类调用成员方法走的子类的
(2)子类和父类出现了一模一样的方法声明,包括方法名,返回值类型,参数列表,这里,父类的方法被子类的方法重写了
2.演示案例:
class AnimalTest{
public static void main(String[] args){
Cat c = new Cat();
c.sleep();
c.eat();
Dog d = new Dog();
d.sleep();
d.eat();
}
}
class Animal{
String name;
int leg;
public void sleep(){
System.out.println("晚上睡觉 ");
}
public void eat(){
System.out.println("吃东西,倍儿香");
}
}
class Cat extends Animal{
public void sleep(){
System.out.println("白天睡觉 ");
}
public void eat(){
System.out.println("吃鱼,倍儿香...");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("吃骨头,倍儿香...");
}
}
运行结果:
3. 结论:
通过子类对象去访问一个方法
首先在子类中找
然后在父类中找
如果还是没有就报错。(不考虑父亲的父亲…)
4. 方法重写
(1)方法重写概述:子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。
(2)使用特点:
如果方法名不同,就调用对应的方法
如果方法名相同,最终使用的是子类自己的
(3)方法重写的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
(4)方法重写的注意事项
父类中私有方法不能被重写
子类重写父类方法时,访问权限不能低于父类的
父类静态方法,子类也必须通过静态方法进行重写。
5. Override和Overload的区别?
Override:重写,子父类中,方法名一样,参数列表不同,返回值类型一样
Overload:重载,同类中,方法名一样,参数列表不同的方法,与返回值类型无关
(九)final关键字
final关键字是最终的意思,当描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这个值起个名字,以便于阅读。而这个值不需要改变,所以加上final修饰。
final可以修饰类,成员变量,成员方法。
修饰类,类不能被继承
修饰变量,变量就变成了常量,只能被赋值一次
修饰方法,方法不能被重写
final修饰变量的初始化时机
在对象构造完毕前即可(显示初始化)
基本类型被修饰后,是其值不能被改变
引用类型被修饰后,是其地址值不能被改变
简单案例演示:
class FinalDemo{
public static void main(String[] args){
final String s = "192.168.36.72";
System.out.println(s);
final int x = 10;//值不能被改变
System.out.println(x);
final int[] arr = {123,4455};//地址值不能被改变
System.out.println(arr);
}
}
二、抽象类
没有办法具体描述的类,比如:水果,工具,蔬菜,情感……
(一)格式:
类:abstract class 类名 { }
方法:public abstract void eat();
注意:抽象方法是没有方法体
抽象类中可以没有抽象方法,但是如果有抽象方法,那么此类必然为抽象类
(一)抽象类特点
1. 抽象类和抽象方法必须用abstract关键字修饰
格式:
类:abstract class 类名 {}
方法:public abstract void eat();
2. 抽象类不一定有抽象方法,可以有非抽象方法,但有抽象方法的类一定是抽象类
3. 抽象类中,有构造方法,但是抽象类不能直接new,也就是不能实例化
4. 那么,抽象类如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。
5. 抽象类的子类
要么是抽象类
要么重写抽象类中的所有抽象方法
(三)抽象类的成员特点
成员变量
可以是变量
也可以是常量
构造方法
有构造方法,但是不能实例化
那么,构造方法的作用是什么呢?
用于子类访问父类数据的初始化
成员方法
可以有抽象方法,限定子类必须完成某些动作
也可以有非抽象方法,提高代码服用性
(四)abstract不能和哪些关键字共存
private 冲突 -- 对子类隐藏,无法被复写,而abstract必须让子类重写
final 冲突 -- 被final修饰后,不能被重写,矛盾
static 无意义 – 类名调用,没有方法体,无意义
(五)老师案例
具体事物:基础班老师,就业班老师
共性:姓名,年龄,讲课。
class TeacherAbstract{
public static void main(String[] args){
BaseTeacher bt=new BaseTeacher("小李广",28);
System.out.println("我叫"+bt.getName()+",今年"+bt.getAge());
bt.teach();
System.out.println("----------------------------------------");
EmploymentTeacher et=new EmploymentTeacher("凌子峰",29);
System.out.println("我叫"+et.getName()+",今年"+et.getAge());
et.teach();
}
}
abstract class Teacher{
//成员变量
private String name;
private int age;
//构造函数
Teacher(){}
Teacher(String name,int age){
this.name=name;
this.age=age;
}
//get/set
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
//抽象方法
public abstract void teach();
}
class BaseTeacher extends Teacher{
BaseTeacher(){}
BaseTeacher(String name,int age){
super(name,age);
}
public void teach(){
System.out.println("我主讲Java_SE course,打好基础好上就业班");
}
}
class EmploymentTeacher extends Teacher{
EmploymentTeacher(){}
EmploymentTeacher(String name,int age){
super(name,age);
}
public void teach(){
System.out.println("我主讲Java_EE course,学好EE好就业");
}
}
运行结果:
三、多态
(一)多态的概述:某一个事物,在不同时刻表现出来的不同状态。
(二)多态前提和体现
有继承关系
有方法重写
有父类引用指向子类对象
(三)成员访问特点
成员变量:编译看左边,运行看左边
成员方法:编译看左边,运行看右边
静态方法:编译看左边,运行看左边
(四)多态的好处和弊端
1. 好处多态的好处
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)
2. 多态的弊端
不能访问子类特有功能
那么我们如何才能访问子类的特有功能呢?
多态中的转型
3. 向上转型
从子到父
父类引用指向子类对象
4. 向下转型
强制:从父到子
父类引用转为子类对象
格式:子类 变量名 = (子类类型)父类 变量名;
好处:
转型之前,父类 p 并不能使用子类的特有功能
转型之后,父类就可以使用子类的功能.
(五)案例演示:钢铁侠变身过程
class IronManDuoTai{
public static void main(String[] args){
//向上转型,从子类-->父类,父类引用指向子类对象
//使用多态创建对象
Person p=new IronMan();
System.out.println("这是谁?");
//输出属性 ,是父类的
System.out.println("我啊,不认识了吗?"+p.name+"啊!");
System.out.println("怎么做起这个了?");
System.out.print("没办法,只能靠");
//调用方法 , 是子类的
p.business();
System.out.println("赚点外快了");
//变身过程,向下转型,强制转换,从父类-->子类
System.out.println("前方有情况,救命啊~~~~~~~");
IronMan im=(IronMan)p;
im.fly();
System.out.print("我是"+im.name+",");
im.savePeople();
}
}
//定衣父类Person
class Person{
String name="托尼.斯塔克";
public void business(){
System.out.println("开厂子,很赚钱");
}
}
//定义子类IronMan
class IronMan extends Person{
String name="钢铁侠";
public void business(){
System.out.print("合影5块");
}
public void fly(){
System.out.println("飞。。。");
}
public void savePeople(){
System.out.println("我来救人");
}
}
运行结果:
四、接口
(一)接口概述:本质就是一种规则
(二)作用:就是扩展类功能
(三)特点:
1. 接口用关键字interface表示
格式:interface 接口名 {}
2. 类实现接口用implements表示
格式:class 类名 implements 接口名 {}
3. 接口不能实例化
4. 那么,接口如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。
5. 接口的子类
要么是抽象类
要么重写接口中的所有抽象方法
6. 接口中可以写方法,但是必须是抽象的.
(四)接口成员特点
成员变量
只能是常量:默认修饰符 public static final
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法:默认修饰符 public abstract
(五)类与类,类与接口以及接口与接口的关系
类与类
继承关系,只能单继承,但是可以多层继承
类与接口
实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口
接口与接口
继承关系,可以单继承,也可以多继承
(六)抽象类和接口的区别
成员区别
抽象类:变量,常量;有抽象方法;抽象方法,非抽象方法
接口:常量;抽象方法
关系区别
类与类:继承,单继承
类与接口:实现,单实现,多实现
接口与接口:继承,单继承,多继承
设计理念区别
抽象类:被继承体现的是:”is a”的关系。共性功能
接口:被实现体现的是:”like a”的关系。扩展功能
(七)教练和运动员案例
乒乓球运动员和篮球运动员。
乒乓球教练和篮球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
class CoachAndPlayer{
public static void main(String[] args){
PPCoach ppc=new PPCoach("刘国梁","棕色",42);
System.out.println("我叫"+ppc.getName()+",今年"+ppc.getAge()+"岁"+",有一双"+ppc.getEyes()+"的眼睛");
ppc.teach();
ppc.eat();
ppc.sleep();
ppc.studyEnglish();
System.out.println("----------------------------");
PPPlayer ppp=new PPPlayer("凌子峰","黑色",20);
System.out.println("我叫"+ppp.getName()+",今年"+ppp.getAge()+"岁"+",有一双"+ppp.getEyes()+"的眼睛");
ppp.play();
ppp.eat();
ppp.sleep();
ppp.studyEnglish();
System.out.println("----------------------------");
BBCoach bbc=new BBCoach("贺子皓","黑色",40);
System.out.println("我叫"+bbc.getName()+",今年"+bbc.getAge()+"岁"+",有一双"+bbc.getEyes()+"的眼睛");
bbc.teach();
bbc.eat();
bbc.sleep();
System.out.println("----------------------------");
BBPlayer bbp=new BBPlayer("韩以风","棕色",21);
System.out.println("我叫"+bbp.getName()+",今年"+bbp.getAge()+"岁"+",有一双"+bbp.getEyes()+"的眼睛");
bbp.play();
bbp.eat();
bbp.sleep();
}
}
abstract class Person{
private String name;
private int age;
private String eyes;
//构造方法
Person(){}
Person(String name,String eyes,int age){
this.name=name;
this.eyes=eyes;
this.age=age;
}
//get/set
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public String getEyes(){
return eyes;
}
public void setEyes(String eyes){
this.eyes=eyes;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
//成员方法
public void eat(){
System.out.println("吃东西");
}
public abstract void sleep();
}
//教练
abstract class Coach extends Person{
Coach(){}
Coach(String name,String eyes,int age){
super(name,eyes,age);
}
public abstract void teach();
public void sleep(){
System.out.println("教练晚上睡觉");
}
}
//运动员
abstract class Player extends Person{
Player(){}
Player(String name,String eyes,int age){
super(name,eyes,age);
}
public abstract void play();
public void sleep(){
System.out.println("运动员晚上睡觉");
}
}
//接口
interface Study{
public abstract void studyEnglish();
}
//乒乓球教练
class PPCoach extends Coach implements Study{
PPCoach(){}
PPCoach(String name,String eyes,int age){
super(name,eyes,age);
}
public void teach(){
System.out.println("我教乒乓球");
}
public void studyEnglish(){
System.out.println("学英语");
}
}
//篮球教练
class BBCoach extends Coach{
BBCoach(){}
BBCoach(String name,String eyes,int age){
super(name,eyes,age);
}
public void teach(){
System.out.println("我教篮球");
}
}
//乒乓球运动员
class PPPlayer extends Player implements Study{
PPPlayer(){}
PPPlayer(String name,String eyes,int age){
super(name,eyes,age);
}
public void play(){
System.out.println("我会玩乒乓球");
}
public void studyEnglish(){
System.out.println("学英语");
}
}
//篮球运动员
class BBPlayer extends Player{
BBPlayer(){}
BBPlayer(String name,String eyes,int age){
super(name,eyes,age);
}
public void play(){
System.out.println("我会玩篮球");
}
}
运行结果:
五、形式参数和返回值问题案例
抽象类作为形式参数,要的是抽象类的子类对象
牌有很多种-- 麻将,三国杀,多米诺骨牌
所有我们牌定义为抽象类
接口作为形式参数,要的是实现了接口的子类对象
牌还有扩展功能 -- 变魔术
class PersonTest{
public static void main(String[] args){
//创建人的对象
Person p = new Person();
//创建纸牌的对象
Card c = new Card();
p.play(c);
//父类牌指向子类麻将
Pai pai = new MaJiang();
p.play2(pai);
//变魔术
Magic m = new Card();
p.play3(m);
}
}
class Person{
//人打纸牌的方法
public void play(Card c){
c. bang();
c.shunzi();
}
//人玩牌
public void play2(Pai p){
p.fun();
}
//人用纸牌变魔术
public void play3(Magic m){
m.moShu();
}
}
//定义接口
interface Magic{
public abstract void moShu();
}
//抽象的牌类
abstract class Pai{
public abstract void fun();
}
//纸牌继承了牌类, 实现了Magic接口
class Card extends Pai implements Magic{
//牌有炸的方法
public void bang(){
System.out.println("炸了, 翻倍...");
}
//牌有顺子的方法
public void shunzi(){
System.out.println("顺出去,没了.....");
}
//娱乐
public void fun(){
System.out.println("打牌虽好,不要沉迷哦!!!");
}
//用牌可以变魔术
public void moShu(){
System.out.println("给女神,变魔术.....");
}
}
class MaJiang extends Pai{
//娱乐
public void fun(){
System.out.println("打麻将虽好,四圈差不多了");
}
}
运行结果:
抽象类作为返回值类型,返回的是抽象类的子类对象
发牌机不光发纸牌,还能发别的牌(麻将)
class FaPaiJiTest{
public static void main(String[] args){
//创建人对象
Person p = new Person();
//获取发牌机
FaPaiJi fpj = p.getFaPaiJi();
Card c = fpj.faPai();
c.tongHuaShun();
c.fun();
Magic m = new Person().getFaPaiJi().faPai3(); //链式编程
m.moShu();
//new Person().getFaPaiJi().faPai3().moShu();
}
}
class Person{
//人使用 发牌机工作
public Card work(FaPaiJi fpj ){
Card c = fpj.faPai();
return c ;// Card
}
//人拿到发牌机
public FaPaiJi getFaPaiJi(){
return new FaPaiJi();
}
}
class FaPaiJi {
//发牌机发牌
public Card faPai(){
Card c = new Card();
return c;
}
public Pai faPai2(){
//返回的是抽象类的子类对象
Pai p = new Card(); //多态
return p;
}
public Magic faPai3(){
//返回的是接口的实现类的子类对象
Magic m = new Card(); //多态
return m;
}
}
//定义接口
interface Magic{
public abstract void moShu();
}
//定义牌类
abstract class Pai{
public abstract void fun();
}
//card 继承牌类 ,实现接口
class Card extends Pai implements Magic{
public void tongHuaShun(){
System.out.println("同花顺.....");
}
public void fun(){
System.out.println("玩牌好好玩~~~");
}
public void moShu(){
System.out.println("给女神,变魔术.....");
}
}
运行结果:
六、包
(一)包的概述:其实就是文件夹,对类进行分类管理
(二)包的划分:
举例:
按类划分:
学生类
增加:create
删除:delete
修改:update
查询:query
老师类
增加:create
删除:delete
修改:update
查询:query
按功能分:
增加:
增加老师,增加学生
删除:
删除老师,删除学生
修改:
修改老师,修改学生
查询:
查询老师,查询学生
(三)定义包的格式
package 包名;
多级包用.分开即可
注意事项:
package语句必须是程序的第一条可执行的代码
package语句在一个java文件中只能有一个
如果没有package,默认表示无包名
(四)带包的类的编译和运行
手动式
a:javac编译当前类文件。
b:手动建立包对应的文件夹。
c:把a步骤的class文件放到b步骤的最终文件夹下。
d:通过java命令执行。注意了:需要带包名称的执行
java cn.itcast.HelloWorld
自动式
a:javac编译的时候带上-d即可
javac -d . HelloWorld.java
b:通过java命令执行。和手动式一样
(五)包的作用
1. 为避免多个类重名的情况,如果出现两个相同名字的类,可通过包将两者区分,从而避免冲突。
2. 对类文件进行分类管理,可以将相关的一些类放在同一个包中。
3. 给类提供多层命名空间,如a包中的Demo.class文件,如果要创建Demo对象,就要在使用上加上a.如:a.Demo demo=new a.Demo();
4. 包的出现可以将java的类文件和源文件相分离。
(六)规则
1. 包必须写在程序的第一行。因为要先有包,才知道类文件的存放地
2. package只有一句,如果不写package,那么默认在当前文件夹
3. 类的全称:包名.类名
4. 编译定义了包的程序文件,在编译时要指定包的存储目录。如:javac —d c:\mypack 类名.java。
(七)导包
1. 导包概述:不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。
2. 导包的格式:
import 包名;
注意:
这种方式导入是到类的名称。
虽然可以最后写*,但是不建议。
例如:
import java.util.Scanner;
import java.util.*;
3. package,import,class的顺序关系
package > import > class
4. 包中的访问
(1)要访问其他包中的类,需要定义类的全称:包名.类名
(2)包如果不在当前路径,需要使用classpath设定环境变量,为JVM指明路径。
(3)被访问的包中的类的权限必须是public。
(4)类中的成员权限:public或者protected,protected是为其他包中的子类提高的一种权限。类共有后,被访问的成员也要共有才可以被访问。不同包中的子类可以直接访问父类中被protected权限修饰的成员。同一包中,protected只作为覆盖。
5. 权限修饰符
public
protected
默认
private
同一类中
可以
可以
可以
可以
同一包子类,其他类
可以
可以
可以
不可以
不同包子类
可以
可以
不可以
不可以
不同包子类
可以
不可以
不可以
不可以
注意:一个java文件里面,不能出现两个以上的共有类或者接口。因为被public修饰的类名必须与java文件名相同。
6. 类及其组成可以用的修饰符
(1)类:
默认,public,final,abstract
我们自己定义:public居多
(2)成员变量:
四种权限修饰符均可,final,static
我们自己定义:private居多
(3)构造方法:
四种权限修饰符均可,其他不可
我们自己定义:public 居多
(4)成员方法:
四种权限修饰符均可,fianl,static,abstract
我们自己定义:public居多
七、内部类
(一)内部类概述:把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。
(二)内部类的访问特点
1. 内部类可以直接访问外部类的成员,包括私有。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式:外部类名.this。
2. 外部类要访问内部类的成员,必须创建对象。当内部类私有就不能再直接创建内部类对象。
(三)内部类位置
按照内部类在类中定义的位置不同,可以分为如下两种格式:
成员位置(成员内部类)
局部位置(局部内部类)
1. 成员内部类
(1)外界如何创建对象
外部类名.内部类名 对象名 = 外部类对象.内部类对象
(2)成员内部的常见修饰符
private 为了保证数据的安全性
static 为了让数据访问更方便
被静态修饰的成员内部类只能访问外部类的静态成员
内部类被静态修饰后的方法
静态方法:直接内部类名.成员方法
非静态方法:创建对象类访问
(3)案例演示
class InnerClass{
public static void main(String[] args){
Outer o=new Outer();
o.display();
System.out.println("------------");
Outer.Inner.show1();
}
}
class Outer{
private int n=1;
static int num=200;
static class Inner{
int n=10;
public void show(){
int n = 120;
System.out.println("n的值为:"+this.n);
System.out.println("n的值为:"+n);
System.out.println("n的值为:"+new Outer().n);
}
public static void show1(){
System.out.println("num的值为:"+num);
}
}
public void display(){
Inner i=new Inner();
i.show();
}
}
运行结果:
2. 局部内部类
(1)概述:局部内部类就是方法内部的类
可以直接访问外部类的成员,在方法内部创建内部类的对象,才能使用到内部类的功能。
在局部位置,可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能,
本质:实现了接口(继承类)的子类匿名对象。
(2)局部内部类访问局部变量的注意事项:
必须被final修饰,为什么呢?因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。通过反编译工具可以看一下。
(3)案例演示
class OuterTest{
public static void main(String[] args){
//创建外部类对象
Outer o=new Outer();
o.method();
System.out.println();
}
}
class Outer{
private int num=2;
public void method(){
final int num=6;//注:从内部类中访问本地变量num;需要被声明为最终类型
//局部内部类
class Inner{
public void show(){
System.out.println("num="+num);
System.out.println("num="+new Outer().num);
}
}
//创建内部类对象
Inner i=new Inner();
i.show();
}
}
运行结果:
(四)匿名内部类
1. 就是内部类的简化写法。
2. 前提:存在一个类或者接口
这里的类可以是具体类也可以是抽象类。
3. 格式:
new 类名或者接口名() {重写方法;}
4. 本质:
是一个继承了类或者实现了接口的子类匿名对象
5. 匿名内部类中定义的方法最好不要超过3个。
6. 匿名内部类的利与弊
好处:简化书写
弊端:
①不能直接调用自己的特有方法。
②不能做强转动作
③如果继承的父类或接口中有很多方法时,使用匿名内部类,阅读性会非常差,而且调用会很麻烦。所以匿名内部类中定义的方法一般不超过3个。
案例演示
class AnonymityTest{
public static void main(String[] args){
Anonymity a=new Anonymity();
a.method();
}
}
//存在一个接口Inter
interface Inter{
public abstract void show();
}
class Anonymity{
public void method(){
new Inter(){
//重写方法
public void show(){
System.out.println("HelloWorld...");
}
}.show();
}
}
运行结果:
7. 加入方法有多个,如何调用呢?
方式1:每一种格式调用一个,太麻烦
方式2:用类或者接口接收该子类对象,多态思想
案例演示:
class AnonymityTest{
public static void main(String[] args){
Anonymity a=new Anonymity();
a.method();
}
}
//存在一个接口Inter
interface Inter{
public abstract void show();
public abstract void show1();
}
class Anonymity{
public void method(){
Inter i=new Inter(){//接口指向子类对象,多态思想
//重写方法
public void show(){
System.out.println("HelloWorld...");
}
public void show1(){
System.out.println("What?");
}
};
i.show();
i.show1();
}
}
运行结果:
8. 接口作为形式参数的案例演示
class PersonTest{
public static void main(String[] args){
PersonStudy ps=new PersonStudy();
ps.show(new Person(){
public void study(){
System.out.println("一起好好学Java~~~~~~");
}
});
System.out.println();
}
}
class PersonStudy{
//接口作为形式参数
public void show(Person p){
p.study();
}
}
interface Person{
public abstract void study();
}
9. 练习题:按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
分析:
(1)Outer.method(),类名.方法名,说明method是静态方法
(2) method().show(),说明 method返回值类型为对象
返回的是Inter的实现类的子类匿名对象
实现代码:
class Outer{
public static Inter method(){
return new Inter(){
public void show(){
System.out.println("HelloWorld");
}
};
}
}
相关文章
- 黑马程序员--JAVA<面向对象>--构造函数、抽象类、接口、多态
- 黑马程序员-Java基础-面向对象—继承、构造函数、重写、final、抽象类、接口
- 黑马程序员--------java面向对象 继承、接口、多态、Object类
- 黑马程序员---java基础之面向对象(一)三大特征(封装,继承,多态)
- 黑马程序员——Java基础——继承之内部类(四)
- 黑马程序员—【Java基础篇】之多态、内部类、异常及包
- 2018/01/09JAVA 基础 / 接口与继承 / JAVA的4种内部类详解
- Java基础之封装、继承、多态、接口详解
- 黑马程序员——Java面向对象(二)之封装、继承、多态、接口等
- 黑马程序员 JAVA基础<二> 面向对象之封装 继承 多态