7.3枚举类型
1.什么是枚举?jdk1.5
枚举在Java中是一种特殊的类,
特殊:这个类型的对象是固定的有限的几个常量对象。
2.案例:
星期week,这个类,它的对象就可以限定在7个。
3.如何声明枚举类?
(1)旧的,JDK1.5之前
A:构造器私有化
B:在类的内部预先创建好n个对象供外界使用
public static final 数据类型 常量对象名 = new 数据类型(【实参列表】);
(2)新的,JDK1.5之后(更推荐)
【修饰符】 enum 枚举类型{
常量对象名,常量对象名(),常量对象名(【实参列表】);
其他成员
}
4、枚举类型特点和要求
(1)枚举类型的构造器一定private
(2)枚举类型中需要预先创建好n个常量对象
(3)使用enum声明的枚举类型,常量对象的指定(本质就是创建)必须在枚举类中的首行。
如果常量对象列表后面没有其他成员了, ; 可以省略
如果常量列表后面还有其他成员, ; 不能省略
(4)使用enum声明的枚举类型,默认的父类是java.lang.Enum类,
当然Enum的父类仍然是Object。而且枚举类型不能再继承别的类了,
也就是说,他的直接父类只能是Enum类。
(5)因为构造器私有化的问题,枚举类没有子类。
5、枚举类的方法
总:除了Object类的方法,也会继承Enum类的方法,
(1)Enum类已经重写过一次toString,当然我们自己的枚举类还可以继续重写。
(2)其他方法
String name()
int ordinal()
static 枚举类型[] values() ==>API中没有
static 枚举类型 valueOf(String) ==>API中没有
...
6、switch-case语句
switch(表达式)的类型:byte,short,int,char,String,枚举
switch(表达式){
case 常量名:
...
【break;】
case 常量名:
...
【break;】
}
public class Week {
public static final Week MONDAY = new Week("星期一");
public static final Week TUESDAY = new Week("星期二");
public final static Week WEDNESDAY = new Week("星期三");
public final static Week THURSDAY = new Week("星期四");
public final static Week FRIDAY = new Week("星期五");
public final static Week SATURDAY = new Week("星期六");
public final static Week SUNDAY = new Week("星期七");
private String description;
private Week(){}
private Week(String description) {
this.description = description;
}
}
public class TestWeek {
public static void main(String[] args) {
/* Week[] weeks = new Week[10]
for (int i = 0
weeks[i] = new Week()
}*/
Week mon = Week.MONDAY
System.out.println(mon)
}
}
public enum Month {
JANUARY("一月"),FEBRUARY(),MARCH,APRIL,MAY,JUNE,JULY,AUGUST,SEPTEMBER,OCTOBER,NOVEMBER,DECEMBER;
private String description;
Month() {
}
Month(String description) {
this.description = description;
}
@Override
public String toString() {
return super.toString() + ": " + description;
}
}
import java.util.Scanner;
public class TestMonth {
public static void main(String[] args) {
Month m = Month.JANUARY;
System.out.println(m);
String name = m.name();
int ordinal = m.ordinal();
System.out.println("name = " + name);
System.out.println("ordinal = " + ordinal);
Month b = Month.FEBRUARY;
System.out.println("二月的name = " + b.name());
System.out.println("ordinal = " + b.ordinal());
System.out.println("-----------------------");
Month[] months = Month.values();
for (int i = 0; i < months.length; i++) {
System.out.println(months[i]);
}
System.out.println("-----------------------");
Scanner input = new Scanner(System.in);
System.out.print("请输入你要的月份名称(大写):");
String monthName = input.next();
Month month = Month.valueOf(monthName);
System.out.println("month = " + month);
switch (month){
case JANUARY:
System.out.println("一月是一年的第一个月份");break;
case FEBRUARY:
System.out.println("二月是龙抬头的月份");break;
}
input.close();
}
}
7.4内部类
回忆:之前学过的
类的成员:成员变量(静态变量和实例变量)
成员方法(静态方法、非静态方法、抽象方法、native方法、final方法)
构造器(无参构造、有参构造)
回忆:
类的定义:一类具有相同特征的事物的抽象描述。
今天:内部类
内部类的分类:
(1)成员内部类:在类中方法外
A:静态成员内部类,简称静态内部类
B:非静态成员内部类,简称成员内部类
(2)局部内部类:在方法体的内部
c:有名字的局部内部类
D:匿名的局部内部类,简称匿名内部类
7.4.1静态内部类
public class Testpublic class TestStaticInnerClass {
public static void main(String[] args) {
Outer.Inner.inTest();
Outer.Inner in = new Outer.Inner();
in.inMethod();
}
}
class Outer{
private static int a = 1;
private static int c;
private int d;
public static class Inner {
private static int a = 2;
private int b;
public void inMethod(){
System.out.println("静态内部类的非静态方法");
System.out.println("外部类.a = " + Outer.a);
System.out.println("a = " + a);
System.out.println("c = " + c);
}
public static void inTest(){
System.out.println("静态内部类的静态方法");
}
}
public static void outMethod(){
System.out.println("静态内部类.a = " + Inner.a);
Inner inner = new Inner();
System.out.println("静态内部类.b = " + inner.b);
}
}
7.4.2 非静态内部类
1、非静态内部类的声明格式
【修饰符】 class 外部类{
【其他修饰符】 class 非静态内部类{
}
}
2、从类的角度
(1)是否有自己独立的字节码文件?
外部类名$非静态内部类名.class
(2)是否可以继承自己的父类,实现自己的父接口?
该父类和父接口与外部类无关。
答案:可以
(3)是否可以有自己的成员
答案:可以
注意:非静态内部类,在JDK16之前,不允许声明自己的静态成员
成员变量(实例变量)
成员方法(非静态方法、抽象方法、native方法、final方法)
构造器(无参构造、有参构造)
成员内部类(但是,不会这么干)
(4)修饰符
外部类的修饰符:public或缺省,final,abstract
成员内部类的修饰符:(多)
权限修饰符:public,protected,缺省,private
其他修饰符:final,abstract
3、从成员的角度
(1)在非静态内部类中使用外部类的静态成员,包括私有的。
可以,直接用
(2)在非静态内部类中使用外部类的非静态成员,包括私有的。
可以,直接用
(3)在外部类中使用非静态内部类的静态成员,包括私有的。(无)
(4)在外部类中使用非静态内部类的非静态成员,包括私有的。
可以,需要通过"非静态内部类的对象名."进行访问
(5)如果非静态内部类不是私有的,在外部类的“外面”是否可以使用?
A:在外面使用非静态内部类的静态成员(无)
B:在外面使用非静态内部类的非静态成员
外部类名 out = new 外部类名(【实参列表】);
外部类名.非静态内部类名 in = out.new 非静态内部类名(【实参列表】);
in.非静态成员
另一种写法:
在外部类中声明一个方法,用于返回内部类的对象。
外部类名 out = new 外部类名(【实参列表】);
外部类名.非静态内部类名 in = out.get内部类();
in.非静态成员
(6)当非静态内部类中的成员与外部类的静态成员重名了,
使用"外部类名.静态成员"表示的是外部类的,
不加"."的表示自己的。
(7)当非静态内部类中的成员与外部类的非静态成员重名了,
使用"外部类名.this.非静态成员"表示的是外部类的,
不加"外部类名.this."的表示自己的。
4、什么情况下要使用静态成员内部类,
什么情况下,只能使用非静态成员内部类?
如果在成员内部类中,需要访问外部类的非静态成员,
那么成员内部类就只能是非静态成员内部类。
如果在成员内部类中,不需要访问外部类的非静态成员,
那么成员内部类就可以是静态成员内部类。
public class TestNonStaticInnerClass {
public static void main(String[] args) {
Outer out = new Outer();
Outer.Inner in = out.new Inner();
in.inMethod();
Outer.Inner in2 = out.getInner();
in2.inMethod();
}
}
class Outer{
private static int a=1;
private int b=1;
private static int c;
private int d;
public class Inner {
private int a = 2;
private int b = 2;
public void inMethod(){
System.out.println("非静态内部类的非静态方法");
System.out.println("外部类的a = " + Outer.a);
System.out.println("外部类的b = " + Outer.this.b);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
public void outMethod(){
Inner in = new Inner();
System.out.println("非静态内部类.a = " + in.a);
System.out.println("非静态内部类.b = " + in.b);
}
public Inner getInner(){
return new Inner();
}
}
7.4.3 局部内部类
1、局部内部类的声明方式:
【修饰符】 class 外部类{
【修饰符】 返回值类型 方法名(【形参列表】){
【其他修饰符】 class 局部内部类{
}
}
}
2、从类的角度
(1)是否有自己独立的字节码文件?
外部类名$编号局部内部类名.class
同一个外部类中,不同的方法内部可能存在同名的局部内部类。
(2)是否可以继承自己的父类,实现自己的父接口?
该父类和父接口与外部类无关。
答案:可以
(3)是否可以有自己的成员
答案:可以
注意:局部内部类,在JDK16之前,不允许声明自己的静态成员
成员变量(实例变量)
成员方法(非静态方法、抽象方法、native方法、final方法)
构造器(无参构造、有参构造)
成员内部类(但是,不会这么干)
(4)修饰符
局部内部类的修饰符:final,abstract
3、从成员的角度
(1)在局部内部类中使用外部类的静态成员,包括私有的。
可以,直接用
(2)在局部内部类中使用外部类的非静态成员,包括私有的。
取决于所在的方法。所在方法是非静态的,就可以直接使用。
(3)在外部类中使用局部内部类的静态成员,包括私有的。(无)
(4)在外部类中使用局部内部类的非静态成员,包括私有的。
严格遵守作用域。仅限于当前方法,而且要在声明之后。
(5)如果“局部内部类”想要在外部类的“外面”使用,是否可以?(不可以)
(6)当局部内部类中的成员与外部类的静态成员重名了,
使用"外部类名.静态成员"表示的是外部类的,
不加"外部类名."的表示自己的。
(7)当局部内部类中的成员与外部类的非静态成员重名了,
使用"外部类名.this.非静态成员"表示的是外部类的,
不加"外部类名.this."的表示自己的。
(8)在局部内部类中可以使用当前方法的局部变量,但是该局部变量只能是final的。
JDK1.8之前,必须手动加final,JDK1.8之后,一旦局部变量被局部内部类使用,会自动加final
为什么局部变量必须是final的,才能被局部内部类使用?(冷门)
原因:
因为局部变量的生命周期是非常短。当前方法运行结束,
局部变量的内存就随着方法出栈自动就消失了。
为了解决这个问题,编译器会在局部内部类中自动添加一个成员变量,
用于存储该局部变量的值。
因为这个操作是编译器自动完成的。
大部分人会以为它们就是同一个变量,值应该始终保持一致。
为了避免“误解”,干脆就规定它们必须是final,值不可以修改。
当初什么值,就是什么值。
package com.atguigu.inner.local;
public class TestLocalInner {
public static void main(String[] args) {
Father obj = Outer.outMethod();
obj.inMethod();
}
}
abstract class Father{
public abstract void inMethod();
}
class Outer{
private static int a;
private int b;
public static Father outMethod(){
final int num = 1;
class Inner extends Father{
private int a;
private int b;
public void inMethod(){
System.out.println("外部类.a = " + Outer.a);
System.out.println("a = " + a);
System.out.println("num = " + num);
}
}
return new Inner();
}
public void outTest(){
class Nei{
private int a;
private int b;
public void inMethod(){
System.out.println("外部类.a = " + Outer.a);
System.out.println("外部类.b = " + Outer.this.b);
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
}
}
7.4.4 匿名内部类
1、匿名内部类
首先是一个局部内部类,所有局部内部类的特点它都有。
其次它没有名字,一次性的。
通常匿名内部类不会很复杂,里面通常只有1个或2个方法,
而且该方法一般都是重写父类或父接口的方法,
很少自定义自己扩展的方法。
2、语法
(1)new 父类(){
}
(2)new 父接口(){
}
(3)new 父类(实参列表){
}
因为匿名内部类是一次性,没有名字,所以必须在类声明的同时,把它唯一的对象创建好。
又因为他没有名字,需要写父类或父接口的名字。
(1)new 父类(){} 的意思是匿名内部类的构造器首行默认调用父类的“无参”构造。
(3)new 父类(实参列表){} 的意思是匿名内部类的构造器首行默认调用父类的“有参”构造。
(2)new 父接口(){}的意思是匿名内部类的构造器首行默认调用父类的“无参”构造。父类是Object.
同时也指明了该匿名内部类的一个父接口。
辨别:
new Object(); ==> 创建Object类本类的对象
new Object(){}; ==> 创建Object的一个匿名子类的对象
3、匿名内部类调用方法的格式
(1)new 父类/父接口(【构造器的实参列表】){
}.方法(【方法的实参列表】);
(2)父类/父接口 变量名 = new 父类/父接口(【构造器的实参列表】){
};
变量名.方法(【方法的实参列表】);
package com.atguigu.inner.anoy;
public class TestInner {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object(){};
System.out.println(o1.getClass());
System.out.println(o2.getClass());
System.out.println("-------------------------");
new Object(){
public void m1(){
System.out.println("Object第二个匿名子类的m1方法");
}
}.m1();
Object o3 = new Object(){
public void m2(){
System.out.println("Object第三个匿名子类的m2方法");
}
};
System.out.println("-------------------------");
Father f = new Father(){
@Override
public void method() {
System.out.println("Father的匿名子类重写Father方法");
}
};
f.method();
f.test();
System.out.println("-------------------------");
Mother m = new Mother(100){
@Override
public void method() {
System.out.println("子类重写Mother的方法");
}
};
m.method();
System.out.println("-------------------------");
Flyable fObject = new Flyable(){
@Override
public void fly() {
System.out.println("实现接口的抽象方法");
}
};
fObject.fly();
fObject.fly();
fObject.fly();
}
}
class Father{
public void method(){
System.out.println("Father.method");
}
public void test(){
System.out.println("Father.test");
}
}
class Mother{
private int num;
public Mother(int num) {
this.num = num;
}
public void method(){
System.out.println("Mother.method" + num);
}
}
interface Flyable{
void fly();
}
7.5 this和super
7.5.1 this和super的含义
1、含义
(1)this是一个变量,表示当前对象。在构造器和非静态方法,非静态代码块(关于非静态代码块后面学习),非静态内部类中,
都会默认有一个this变量。
A:构造器:表示正在new的那个对象
class Father{
private int num;
public Father(){
System.out.println(this);
}
public Father(int num){
this.num = num;
}
}
class Son extends Father{
public Son(){
super();
}
public Son(int num){
super(num);
}
}
①Father f = new Father(); ==> this是Father类对象
②Father f = new Son(); ==> this是Son类的对象
好比:
xx :"我爱你!";
B:非静态方法中
this代表正在调用该方法的对象
class Father{
public void method(){
System.out.println(this);
}
}
class Son extends Father{
}
①Father f = new Father();
f.method(); ==> this是Father类对象
②Father f = new Son();
f.method(); ==> this是Son类的对象
C:非静态内部类
class Outer{
class Inner{
public void method(){
System.out.println(this);
System.out.println(Outer.this);
}
}
}
(2)super不是一个变量,不能独立使用。仅仅是一个关键字而已,用于“复用”父类声明的xx代码。
super的使用是依赖于当前对象,即当前对象要完成xx事,复用父类的xx代码。
7.5.2 this的用法
2、用法
this:
this的编译时类型是本类类型。
this的运行时类型要看“当前对象”的运行时类型。
(1)this:独立使用
(2)this.成员变量
当局部变量与成员变量重名了,那么在成员变量前面加"this."
如果没有重名问题,完全可以省略this.
(3)this.成员方法:完全可以省略this.
编译时:表示调用本类的或父类的未被重写的xx方法。
运行时:
this代表的是本类的对象,那么执行本类的xx方法
this代表的是子类的对象,那么要看子类是否重写了该方法,如果重写了,就执行子类重写的方法。
(4)this() 或 this(实参列表):调用“本类”的构造器,必须在构造器的首行。
(5)外部类名.this.非静态成员:用于在内部类中访问外部类的非静态成员
7.5.3 super的用法
3、super
前提条件:通过super复用的构造器、成员变量、成员方法等都必须在子类可见。
(1)super.成员变量
当子类声明了和父类重名的成员变量时,可以用“super.成员变量”表示引用父类声明的那个成员变量。
注意:实际开发中要避免子父类声明重名的成员变量。
(2)super.成员方法
当子类“重写”了父类某个方法,子类又想要“复用”父类被重写的方法的方法体,
就可以使用”super.被重写方法“,否则会出现死循环递归,导致内存溢出。
要是父类的方法没有被子类重写,完全不需要加"super."。
(3)super()和super(实参列表):在子类的构造器首行,必须调用父类的构造器。
为什么要调用父类的构造器?复用父类构造器的代码为 从父类继承的成员变量进行初始化。
this() 或 this(实参列表)与 super()和super(实参列表)只能四选一。
如果都没写,默认是super();
(4)父接口名.super.默认方法名:解决冲突问题
7.5.4 变量的访问原则
4、变量的访问
(1)变量的前面如果既没有this.,也没有super.的时候,如果辨别它访问的是谁?
A:先看局部变量
B:再看本类的成员变量
C:再看父类的成员变量
从A->B->C,找到为止。
(2)变量的前面有this.的时候,如果辨别它访问的是谁?
B:先看本类的成员变量
C:再看父类的成员变量
从B->C,找到为止。
(3)变量的前面有super.的时候,如果辨别它访问的是谁?
C:直接看父类的成员变量
package com.atguigu.keyword;
public class TestVariable {
public static void main(String[] args){
Zi son = new Zi();
son.test();
son.method(30,13);
}
}
class Fu{
int a = 10;
int b = 11;
}
class Zi extends Fu{
int a = 20;
public void test(){
System.out.println("子类的a:" + a);
System.out.println("子类的a:" + this.a);
System.out.println("父类的a:" + super.a);
System.out.println("b = " + b);
System.out.println("b = " + this.b);
System.out.println("b = " + super.b);
}
public void method(int a, int b){
System.out.println("局部变量的a:" + a);
System.out.println("子类的a:" + this.a);
System.out.println("父类的a:" + super.a);
System.out.println("局部变量的b = " + b);
System.out.println("b = " + this.b);
System.out.println("b = " + super.b);
}
}
7.5.5 this和super综合面试题
面试题1
package com.atguigu.exam1;
public class TestOne {
public static void main(String[] args) {
Son s = new Son();
s.test();
System.out.println("----------------");
Daughter d = new Daughter();
d.test();
}
}
class Father {
protected int num = 10;
public int getNum() {
return num;
}
}
class Son extends Father {
private int num = 20;
public void test() {
System.out.println(getNum());
System.out.println(this.getNum());
System.out.println(super.getNum());
}
}
class Daughter extends Father {
private int num = 20;
@Override
public int getNum() {
return num;
}
public void test() {
System.out.println(getNum());
System.out.println(this.getNum());
System.out.println(super.getNum());
}
}