javaSE
属性和方法
方法注意事项
- 方法应该定义在类当中,但是不能在方法当中再定义方法,不能嵌套。
- 方法定义的前后顺序无所谓。
- 方法定义之后不会执行,如果希望执行,一定要调用:单独调用、打印调用、赋值调用。
- 如果方法有返回值,那么必须写上“return 返回值;”,不能没有。
- return后面的返回值数据,必须和方法的返回值类型,对应起来。
- 对于一个void没有返回值的方法,不能写return后面的返回值,只能写return自己。
- 对于void方法当中最后一行的return可以省略不写。
- 一个方法当中可以有多个return语句,但是必须保证同时只有一个会被执行到。
成员变量和局部变量的区别
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中方法外 | 方法内或者方法声明(形参)上 |
内存中位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的存在而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的调用完毕而消失 |
初始化值不同 | 有默认的初始化值 | 没有默认的初始化值,必须先定义,赋值,才能使用 |
方法重载
public class OverLoadingTest { public static void main(String[] args) { // System.out.println(sumTwo(10, 10)); // System.out.println(sumThree(10, 10, 10)); // System.out.println(sumFour(10, 10, 10, 10)); // sum(int a, int b) System.out.println(sum(1, 2)); // sum(double a, double b) System.out.println(sum(1.8, 2.8)); // sum(int a, double b) System.out.println(sum(1, 2.8)); // sum(double a, int b) System.out.println(sum(1.8, 2)); // sum(int a, int b, int c) System.out.println(sum(1, 2,4)); } // public static int sumTwo(int a, int b) { // return a + b; // } // public static int sumThree(int a, int b, int c) { // return a + b + c; // } // public static int sumFour(int a, int b, int c, int d) { // return a + b + c + d; // } public static int sum(int a, int b) { return a + b; } public static int sum(double a, double b) { return (int)(a + b); } public static int sum(int a, double b) { return (int)(a + b); } public static int sum(double a, int b) { return (int)(a + b); } public static int sum(int a, int b, int c) { return a + b + c; } // // 几种错误写法 // // // 重写与参数名称无关 // public static int sum(int x, int y) { // return x + y; // } // // // 重写与返回值类型无关 // public static double sum(double a, double b) { // return a + b; // } } /* 判断下列是否为有效重载 */ 1. public static void open() {} // 正确重就 2. public static void open(int a) {} // 正确重载 3. static void open(int a,int b) {} // 与8冲突 4. public static void open(double a,int b) {} // 正确重载 5. public static void open(int a,double b) {} // 与6冲突 6. public void open(int i,double d) {} // 与5冲突 7. public static void OPEN() {} // 不是重载 8. public static void open(int i,int j) {} // 与3冲突
构造方法
构造方法是一种特殊的方法
功能:主要是完成对象数据的初始化
作用:创建对象
// 格式 public class 类名{ 修饰符 类名(参数) { } }
注意事项:
构造方法的创建
- 如果没有定义构造方法,系统将给出一个默认的无参数构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
构造方法的重载
- 如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
推荐的使用方式
- 无论是否使用,都手工书写无参数构造方法
继承
直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找。
间接通过成员方法访问成员变量:该方法属于谁,就优先用谁,没有则向上找。
public class Fu { int numFu = 10; int numCom = 100; public void methodFu() { System.out.println(numCom); } public void methodCom() { System.out.println(100); } }
public class Zi extends Fu { int numZi = 20; int numCom = 50; public void methodZi() { System.out.println(numCom); } public void methodCom() { System.out.println(50); } }
public static void main(String[] args) { Fu fu = new Fu(); System.out.println(fu.numFu); Zi zi = new Zi(); System.out.println(zi.numFu); System.out.println(zi.numZi); System.out.println(zi.numCom); System.out.println(==========); zi.methodFu(); // 100 zi.methodZi(); // 50 zi.methodCom(); // Fu 100, Zi 50 --> 50 }
局部变量、本类的成员变量、父类的成员变量(同名)
public class Zi extends Fu { int num =20; public void method() { int num = 30; System.out.println(num); System.out.println(this.num); System.out.println(super.num); } }
public class Fu { int num = 10; }
public static void main(String[] args) { Zi zi = new Zi(); zi.method(); }
注意事项:无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。
重写与重载
重写(override):方法的名称一样,参数列表【也一样】,覆盖、覆写。
重载(overload):方法的名称一样,参数列表【不一样】。|
方法覆盖重写的注意事项:
1.必须保证父子类之间方法的名称相同,参数列表也相同。
@override:写在方法前面,用来检测是不是有效的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。
2.子类方法的返回值必须【小于等于】父类方法的返回值范围。
小扩展提示:java.lang.object类是所有类的公共最高父类(祖宗类),java.Lang.String就是object的子类。
3.子类方法的权限必须【大于等于】父类方法的权限修饰符。
小扩展提示:public > protected >(default) > private
备注:(default)不是关键字default,而是什么都不写,留空。
public class NewPhone extends Phone { @Override public void show() { super.show(); // 继承原有功能 System.out.println(显示姓名); // 添加新功能 System.out.println(显示头像); // 添加新功能 } }
继承关系中,父子类构造方法的访问特点:
- 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
- 子类构造可以通过super关键字来调用父类重载构造。
- super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。
- 子类必须调用父类构造方法,不写则默认 super();写了则用写的指定的super调用,super只能有一个,且必须是第一个。
继承的三个特点
封装
封装,它指的是将对象的状态信息隐藏在对象的内部,不允许外部的程序直接访问对象内部的信息。而是通过对象的方法来实现对其内部信息的访问。也就是说:封装实现了私有化属性,和公有化方法。封装通过修饰符有效地保护了对象内部的属性。
权限修饰符(访问修饰符)
在 Java 语言中,提供了三种访问修饰符来对访问的权限进行控制,它们分别是:private, protected, puablic 关于类中的属性的访问权限的控制:
privare修饰符:如果一个类里面的成员(成员变量、方法、构造器)被 private 修饰的时候,这个成员只能在类的内部被访问。这个修饰符把属性隐藏在了内部,对外不可见。
default:它是一种默认的访问权限控制,也就是什么修饰符都不加的状态,它可以被相同的包下的其他类访问。
protected修饰符:如果一个类里面的成员(成员变量、方法、构造器)被 protected 修饰的时候,这个成员它既可以被同一个包中的其他类访问,也可以被不同包中的子类访问。
public 修饰符:它是公共的访问权限,也是最开放的。它是最为宽松的访问的级别。如果一个类的里面的成员(成员变量、方法、构造器),它是可以被所有的类访问的,而不用考虑这个访问的类所处的包的位置,或是与当前类是否有继承关系。
public | protected | (default) | private | |
---|---|---|---|---|
同一个类 | YES | YES | YES | YES |
同一个包 | YES | YES | YES | NO |
不同包子类 | YES | YES | NO | NO |
不同包非子类 | YES | NO | NO | NO |
public class JavaEncap { public static void main(String[] args) { Student1 student1 = new Student1(); student1.name = Kite; student1.age = 24; System.out.println(student1.name); System.out.println(student1.age); System.out.println(---------------------------); Student2 student2 = new Student2(); // student2.setName(KITER); // student2.setAge(-26); student2.setName(KITE); student2.setAge(26); System.out.println(student2.getName()); System.out.println(student2.getAge()); } } class Student1 { String name; int age; } class Student2 { // 通过 private 修饰符将对象的内部属性定义为私有 private String name; private int age; // 需要提供一种方式来让外部进行访问。 // 对象内部的属性,它可能被外部修改,也可能被外部读取。 public void setName(String name) { // 在方法类中实现了对属性的检查与保护 if (name.length() > 4 || name.length() < 2) { System.out.println(您输入的名字应该在2~4个字符之间); return; } this.name = name; } public String getName() { return this.name; } public void setAge(int age) { // 在方法类中实现了对属性的检查与保护 if (age > 100 || age < 0) { System.out.println(您输入的年龄应该在0~100之间); return; } this.age = age; } public int getAge() { return this.age; } }
多态
多态是对象的多态
代码当中体现多态性——父类引用指向子类对象
父类名称 对象名 = new 子类名称();
或者
接口名称 对象名 = new 实现类名称();
访问成员变量的两种方式
- 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找
- 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找
- 常量不可重写
public static void main(String[] args) { // 使用多态的写法 // 左侧父类的引用,指向了右侧子类的对象 Father obj = new Son(); // Son obj2 = new Father(); // 直接报错 obj.method(); // 子类重写父类,使用子类方法 System.out.println(obj.num); // 输出父类定义的数值 // obj.methodSon(); // 子类独有方法量不可传递(父类中找不到会向上找) // System.out.println(obj.age); // 子类独有变量不可传递(父类中找不到会向上找) obj.methodFather(); // 优先等号左边 obj.showNum(); // 若子类重写,优先调用子类(若子类重写方法,缺失num,调用父类) }
在多态的代码中,成员方法的访问规则是:
- 看 new 的是谁,就先用谁,没有则向上找。
口诀:
- 成员变量:编译看左,运行看左。
- 成员方法:编译看左,运行看右。
多态的优势
对象的向上转型
缺点:向上转型为父类,子类特有的方法不再可执行。(采用向下转型——还原)
对象的向下转型
public static void main(String[] args) { Father obj = new Daughter(); obj.method(); if (obj instanceof Son) { Son son = (Son) obj; son.method(); } if (obj instanceof Daughter) { Daughter daughter = (Daughter) obj; daughter.method(); } }
如果转型对应错误,异常类型为:ClassCastException
关键字
abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while
import package public private default protected class interface abstract extends implements stasic super this throw throws new void boolean byte short int float double long char if else while do for switch case break continue try catch finally final return goto assert synchronized transient volatile instanceof native enum const strictfp
static
static 修饰成员变量时
public static void main(String[] args) { Student p1 = new Student(Kite, 25); p1.room = 101教室; // 更推荐 Student.room = 101教室; System.out.println(p1.getName() + , + p1.getAge() + , + p1.room + , + p1.getId()); Student p2 = new Student(Lee, 89); System.out.println(p2.getName() + , + p2.getAge() + , + p2.room + , + p2.getId()); }
public class Student { private String name; private int age; private int id; // private int idCount; // idCount 要使用static修饰 private static int idCount; static String room; public DemoKeywords_Static_Student() { this.id = ++idCount; } public DemoKeywords_Static_Student(String name, int age) { this.name = name; this.age = age; this.id = ++idCount; } 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 int getId() { return id; } }
static 修饰成员方法时
静态方法不属于对象而是属于类的
如果没有 static 关键字,那么必须首先创建对象,然后通过对象才能使用
public static void main(String[] args) { MyClass myClass = new MyClass(); myClass.method(); myClass.methodStatic(); // 不推荐 MyClass.methodStatic(); // 推荐 }
静态不能直接访问非静态。
静态方法中不能使用this关键字
public class MyClass { int num; static int numStatic; // 成员方法 public void method() { System.out.println(这是一个普通的成员方法); // 成员方法可以访问成员变量 System.out.println(num); // 成员方法可以访问静态变量 System.out.println(numStatic); } // 静态方法 public static void methodStatic() { System.out.println(这是一个静态方法); // 静态方法可以访问静态变量 System.out.println(numStatic); // 静态方法不可以访问成员变量 // System.out.println(num); // 静态方法中不能使用this关键 // System.out.println(this.num); } }
this
this修饰的变量用于指代成员变量
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
什么时候使用this呢?解决局部变量隐藏成员变量
this:代表所在类的对象引用--方法被哪个对象调用,this就代表哪个对象
局部变量、本类的成员变量、父类的成员变量代码参见本文:继承 ->局部变量、本类的成员变量、父类的成员变量(同名)
在本类的成员方法中,访问本类的成员变量。
在本类的成员方法中,访问本类的另一个成员方法。
在本类的构造方法中,访问本类的另一个构造方法。
this(...)调用也必须是构造方法的第一个语句,唯一 一个。
B.super和this两种构造调用,不能同时使用。
public class Zi extends Fu { int num = 20; // 3 public Zi() { this(100); // 无参构造调用有参构造(可反向,但不可循环调用),不可调用两次,且不可与 super 同时使用(首行限制) // this(a); } public Zi(int a) {} public Zi(String b) {} // 1 public void showNum() { int num = 10; System.out.println(num); // 10 System.out.println(this.num); // 20 System.out.println(super.num); // 30 } // 2 public void methodA() { System.out.println(AAAAA); } public void methodB() { this.methodA(); // 调用本类其他方法 System.out.println(BBBBB); } }
super
super 关键字的用法:
- 在子类的成员方法中,访问父类的成员变量。
- 在子类的成员方法中,访问父类的成员方法。
- 在子类的构造方法中,访问父类的构造方法。
public class NewPhone extends Phone { @Override public void show() { super.show(); // 继承原有功能 System.out.println(显示姓名); // 添加新功能 System.out.println(显示头像); // 添加新功能 } }
继承关系中,父子类构造方法的访问特点:
- 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
- 子类构造可以通过super关键字来调用父类重载构造。
- super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。
- 子类必须调用父类构造方法,不写则默认 super();写了则用写的指定的super调用,super只能有一个,且必须是第一个。
对应的代码:
public class Zi extends Fu { int num = 10; public Zi() {} public void show() { int num = 20; System.out.println(num); // 20 System.out.println(this.num); // 10 System.out.println(super.num); // 30 } public void method() { super.method() System.out.println(AAAAA); } } //====================================== public static void main(String[] args) { Zi zi = new Zi(); zi.show(); zi.method(); }
final
final 关键字代表最终不可改变的
常见四种用法:
- 可以用来修饰一个类
- 可以用来修饰一个方法
- 可以用来修饰一个局部变量
- 可以用来修饰一个成员变量
当 final 关键字用来修饰一个类时
public final class 类名称 { // ... }
含义:
当前这个类不能有任何的子类(太监类)
final 类无法被覆盖重写
当 final 关键字用来修饰成员方法时
当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写。
修饰符 final 返回值类型方法名称(参数列表){ //方法体 }
注意事项:对于类、方法来说,abstract 关键字和 final 关键字不能同时使用,因为矛盾。
当 final 关键字用来修饰局部变量时
对于基本类型来说,不可变说的是变量当中的数据不可改变
对于引用类型来说,不可变说的是变量当中的地址值不可变
public static void main(String[] args) { int num1 = 10; System.out.println(num1); num1 = 20; System.out.println(num1); // final 修饰 final int num2 = 100; System.out.println(num2); // num2 = 200; // 错误写法 // num2 = 100; // 错误写法 final int num3; num3 = 1000; // 正确写法 // num3 = 1; // 错误写法 System.out.println(------------------------------------); Student student1 = new Student(二木); System.out.println(student1.getName()); student1 = new Student(Kite); System.out.println(student1.getName()); System.out.println(------------------------------------); final Student s2 = new Student(XYZ); // s2 = new Student(ABC); // 错误, final 修饰的引用类型变量地址值不可改变 System.out.println(s2.getName()); // XYZ s2.setName(ABC); System.out.println(s2.getName()); // ABC }
当 final 关键字用来修饰成员变量时
对于成员变量来说,如果使用 final 关键字修饰,那么这个变量不可变。
由于成员变量具有默认值,所以用了 final 之后必须手动赋值,不再给默认值
对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
构造方法赋值必须保证类当中所有重载的构造方法都最终会对final的成员变量进行赋值。
// 1. 直接赋值 private final String name; // 错误写法 private final String name = ABC; // 2. 通过构造方法 // 必须保证类当中所有重载的构造方法最终都会对final的成员变量进行赋值 public class Person { private final String name; public Person() { name = kite; } public Person(String name) { this.name = name; } public String getName() { return name; } // public void setName(String name) { // this.name = name; // } }
接口和抽象类
抽象类
抽象方法:就是加上 abstract 关键字,去掉大括号,直接分号结束。
抽象类: 抽象方法所在的类,必须是抽象类,在 class 之前加上 abstract
如何使用抽象类和抽象方法:
- 不能之间创建(new) 抽象类方法
- 必须用一个子类来继承抽象父类
- 子类必须覆盖重写抽象父类中的所有抽象方法
- 覆盖重写(实现):子类去掉抽象方法的关键字 abstract ,然后补上方法体大括号。
- 创建子类对象进行使用。
注意事项
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
- 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
- 理解:子类的构造方法中,有默认的super(0,需要访问父类构造方法。
- 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
- 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
- 理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
接口
接口就是多个类的公共规范。
接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。
public interface 接口名称 { //接口内容 }
备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java-->.class
- 如果是Java7,那么接口中可以包含的内容有:
-
- 常量
- 抽象方法
- 如果是Java 8,还可以额外包含有:
-
- 默认方法
- 静态方法
- 如果是Java 9,还可以额外包含有:
- 私有方法
接口的抽象方法
接口使用步骤:
-
接口不能直接使用,必须有一个实现类来实现该接口
public class ClassName implements 接口名称 { // ... }
-
接口的实现类必须覆盖重写(实现)接口中的所有的抽象方法。
- 实现:去掉 abstract 加上方法体大括号
-
创建实现类的对象,进行使用
注意事项:
-
接口当中的抽象方法,修饰符必须是两个固定的关键字: public abstract
-
这两个关键字修饰符可以省略,但不可以更改。
public interface MyInterfaceAbstract { public abstract void methodAbs1(); // 不省略 abstract void methodAbs2(); // 省略 public public void methodAbs3(); // 省略 abstract void methodAbs4(); // 省略 public 和 abstract }
接口的默认方法 default
从 java 8 开始,接口里允许定义默认方法
public default 返回值类型 方法名称(参数列表) { 方法体; }
// 接口 public interface MyInterfaceDefault { // 抽象方法 public abstract void methodAbs(); // A B 实现接口后,又新增一个抽象方法 // public abstract void methodAbs2(); // 如果 AB 不重写此方法,则会报错 // 新添加的方法, 使用 default public default void methodAbsDefault() { System.out.println(这是新添加的默认方法); } } /////////////////////////////////////////////////////////// // A 实现 public class MyInterfaceDefaultA implements MyInterfaceDefault{ @Override public void methodAbs() { System.out.println(实现了抽象方法: AAA); } } // B 实现 public class MyInterfaceDefaultB implements MyInterfaceDefault{ @Override public void methodAbs() { System.out.println(实现了抽象方法: BBB); } @Override public void methodAbsDefault() { System.out.println(重写覆盖了接口的默认方法); } }
// main方法 public static void main(String[] args) { MyInterfaceDefaultA myInterfaceDefaultA = new MyInterfaceDefaultA(); // 调用实现的方法 myInterfaceDefaultA.methodAbs(); // 调用接口默认方法 myInterfaceDefaultA.methodAbsDefault(); MyInterfaceDefaultB myInterfaceDefaultB = new MyInterfaceDefaultB(); // 调用实现的方法 myInterfaceDefaultB.methodAbs(); // 重写覆盖接口默认方法 myInterfaceDefaultB.methodAbsDefault();
补充:
- 接口当中的默认方法,可以解决接口升级的问题
- 使用 lambda 表达式和函数式编程的时候,接口当中的默认方法可以拼接函数模型
接口的静态方法 static
从 Java 8 开始,接口当中允许定义静态方法
public interface MyInterfaceStatic { public static void methodStatic() { System.out.println(这是接口的静态方法。); } }
public static void main(String[] args) { // 创建了实现类对象 // MyInterfaceStaticImpl myInterfaceStaticImpl = new MyInterfaceStaticImpl(); // myInterfaceStaticImpl.methodStatic(); // 错误写法 // 直接通过接口调用其静态方法 !!!!!! 注意 MyInterfaceStatic.methodStatic(); } }
接口的私有方法 private
从 java 9 开始,接口中允许定义私有方法。
- 普通私有方法,解决多个默认方法之间重复代码问题
private 返回值类型方法名称(参数列表){ 方法体 }
- 静态私有方法,解决多个静态方法之间重复代码问题
private static 返回值类型 方法名称(参数列表){ 方法体 }
接口的常量
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
public static final 数据类型 常量名称=数据值;
注意事项:
接口当中的常量,可以省略
public static final
,注意:不写也照样是这样。接口当中的常量,必须进行赋值;不能不赋值。
接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
接口总结:
-
成员变量其实是常量,格式:
[public] [static] [final] 数据类型常量名称=数据值;常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。
-
接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型方法名称(参数列表);
注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。
-
从Java8开始,接口里允许定义默认方法,格式:
[public]default返回值类型方法名称(参数列表){方法体}
注意:默认方法也可以被覆盖重写
-
从Java8开始,接口里允许定义静态方法,格式:
[public]static返回值类型方法名称(参数列表){方法体}
注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
-
从Java9开始,接口里允许定义私有很乏,格式:
普通私有方法:private返回值类型方法名称(参数列表){方法体}
静态私有方法:private static 返回值类型方法名称(参数列表){方法体}
注意:private的方法只有接口自己才能调用,不能被实现类或别人使用
使用接口的时候,需要注意:
- 接口是没有静态代码块或者构造方法的
- 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB{
// 覆盖重写所有抽象方法
}- 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写
- 如果实现类有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类
- 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
- 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法
- 类与类之间是单继承的,直接父类只有一个
- 类与接口之间是多实现的,一个类可以实现多个接口
- 接口与接口之间是多继承的
- 多个父接口当中的抽象方法如果重复,没关系
- 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】
内部类
成员内部类
修饰符 class 外部类名称{ 修饰符 class 内部类名称{ //… } //… } // 注意:内用外,可以随意访问; 外用内,需要内部类对象。
内部类编译文件格式 :
public class Body { // 内部类 public class Heart { // 内部类的方法 public void beat() { System.out.println(内部心脏跳动...); System.out.println(我是: + name); } } // 外部类的成员变量 private String name; // 外部类的成员方法 public void methodBody() { System.out.println(外部类的方法); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
成员内部类的使用:
如何使用成员内部类?有两种方式:
1. 间接方式
在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
// 外部类的成员方法 public void methodBody() { System.out.println(外部类的方法); new Heart().beat(); // Heart 为成员内部类 }
2. 直接方式
// 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称(); Body.Heart heart = new Body().new Heart();
同名变量访问:
public class Outer { int num = 10; // 外部类成员变量 public class Inner{ int num = 20; // 内部类成员变量 public void methodInner() { int num = 30; // 内部类方法的局部变量 System.out.println(num); // 局部变量,就近原则 System.out.println(this.num); // 内部类的成员变量 System.out.println(Outer.this.num); // 外部类的成员变量 } } }
局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
修饰符 class 外部类名称 { 修饰符 返回值类型 外部类方法名称(参数列表){ cLass 局部内部类名称 { //... } } }
public class Outer { public void methodOuter() { class Inner { // 局部内部类 int num = 10; public void methodInner() { System.out.println(num); // 10 } } Inner inner = new Inner(); inner.methodInner(); } } public static void main(String[] args) { Outer outer = new Outer(); outer.methodOuter(); }
定义一个内部类或者含有内部类的外部类的时候,权限修饰符规则:
- 外部类:public/(default)
- 成员内部类:public / protected /(defdult)/ private
- 局部内部类:什么都不能写(与 defdult 不同)
匿名内部类(属于局部内部类的一种)
如果接口的实现类(或者是父类的子类)只需要唯一的一次使用
那么这种情况就可以省略掉该类的定义,而改为使用【匿名内部类】
public static void main(String[] args) { // 实现接口 // MyInterfaceImpl myInterface = new MyInterfaceImpl(); // 这种写法也可以 MyInterface myInterface = new MyInterfaceImpl(); // 这种写法也可以 myInterface.method1(); // 匿名内部类 MyInterface obj = new MyInterface() { @Override public void method1(){ System.out.println(匿名内部类实现了接口方法1); } @Override public void method2(){ System.out.println(匿名内部类实现了接口方法2); } }; obj.method2(); new MyInterface() { @Override public void method1() { int num = 1; System.out.println(匿名内部类实现了接口方法 + num); } @Override public void method2() { System.out.println(匿名内部类实现了接口方法2); } }.method1(); }
注意事项
new 接口名称() {...}
new
代表创建对象的动作接口名称
匿名内部类要实现的接口名称{...}
匿名内部类的内容
另外还要注意几点问题:
- 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
- 如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类了。
- 匿名对象,在【调用方法】的时候,只能调用唯——次。
- 如果希望同一个对象,调用次方法,那么必须给对象起个名字。
- 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
- 强调:匿名内部类和匿名对象不是一回事!!!
枚举
可以使用枚举的情况
- 类的对象只有有限个,确定的。举例如下:
- 星期:Monday(星期一)、……、Sunday(星期天)
- 性别:Man(男)、Woman(女)
- 季节:Spring(春节).……Winter(冬天)
- 支付方式:Cash(现金)、WeChatPay(微信)、Alipay(支付宝)、BankCard(银行卡)、CreditCard(信用卡)
- 就职状态:Busy、Free、Vocation、Dimission
- 订单状态:Nonpayment(未付款)、Paid(已付款)、Fulfilled(已配货)、Delivered(已发货)、Return(退货)、Checked(已确认)
- 线程状态:创建、就绪、运行、阻塞、死亡
- 当需要定义一组常量时,强烈建议使用枚举类
- 若枚举只有一个对象,则可以作为一种单例模式的实现方式
用类实现枚举
package com.lee.enum_; public class SeasonTest { // 测试 public static void main(String[] args) { Season spring = Season.SPRING; System.out.println(spring); } } // 自定义枚举类型 class Season { // 1. 声明 Season 对象的属性 : private final 修饰 private final String seasonName; private final String seasonDesc; // 2. 私有化类的构造器,并给对象属性赋值 private Season(String seasonName, String seasonDesc) { this.seasonName = seasonName; this.seasonDesc = seasonDesc; } // 3. 提供当前枚举类的多个对象 : public static final 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 String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } @Override public String toString() { return Season{ + seasonName=' + seasonName + '\'' + , seasonDesc=' + seasonDesc + '\'' + '}'; } }
枚举
package com.lee.enum_; public class SeasonTest02 { // 测试 public static void main(String[] args) { Season2 winter = Season2.WINTER; System.out.println(winter); // WINTER System.out.println(winter.getSeasonName()); // 冬天 System.out.println(winter.getSeasonDesc()); // 冰天雪地 } } // 自定义枚举类型 enum enum Season2 { // 3. 提供当前枚举类的多个对象 SPRING(春天, 春暖花开), SUMMER(夏天, 夏日炎炎), AUTUMN(秋天, 秋高气爽), WINTER(冬天, 冰天雪地); // 1. 声明 Season 对象的属性 : private final 修饰 private final String seasonName; private final String seasonDesc; // 2. 私有化类的构造器,并给对象属性赋值 private Season2(String seasonName, String seasonDesc) { this.seasonName = seasonName; this.seasonDesc = seasonDesc; } public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } // @Override // public String toString() { // return Season{ + // seasonName=' + seasonName + '\'' + // , seasonDesc=' + seasonDesc + '\'' + // '}'; // } }
相比于 class :
public static final Season SPRING = new Season(春天, 春暖花开); --> SPRING(春天, 春暖花开),
多个对象之间用 , 隔开
不重写 toString 可以打印出 对象名
枚举的主要方法
方法名 | 详细描述 |
---|---|
valueOf | 传递枚举类型的Class对象和枚举常量名称给静态方法valueOf,会得到与参数匹配的枚举常量。 |
toString | 得到当前枚举常量的名称。你可以通过重写这个方法来使得到的结果更易读。 |
equals | 在枚举类型中可以直接使用“==”来比较两个枚举常量是否相等。Enum提供的这个equals0方法,也是直接使用 ==“实现的。它的存在是为了在Set、List和Map中使用。注意,equals()是不可变的。 |
hashCode | Enum实现了hashCode0来和equals()保持一致。它也是不可变的。 |
getDeclaringClass | 得到枚举常量所属枚举类型的Class对象。可以用它来判断两个枚举常量是否属于同一个枚举类型。 |
name | 得到当前枚举常量的名称。建议优先使用toString()。 |
ordinal | 得到当前枚举常量的次序。 |
compareTo | 枚举类型实现了Comparable接口,这样可以比较两个枚举常量的大小(按照声明的顺序排列) |
clone | 枚举类型不能被Clone。为了防止子类实现克隆方法,Enum实现了一个仅抛出CloneNotSupportedException 异常的不变Clone()。 |
enum 常用方法
// enum 常用方法 // 1. values Season2[] seasons = Season2.values(); for (Season2 season : seasons) { System.out.print(season + || ); } // SPRING || SUMMER || AUTUMN || WINTER || System.out.println(); // Thread 的状态也是枚举类型 Thread.State[] states = Thread.State.values(); for (Thread.State state : states) { System.out.print(state + ); } // NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATED System.out.println(); // 2. valueOf Season2 winter1 = Season2.valueOf(WINTER); System.out.println(winter1); // Season2 winter2 = Season2.valueOf(WINTER___); // IllegalArgumentException: No enum constant com.lee.enum_.Season2.WINTER___ // System.out.println(winter2); // 3. toString // System.out.println(winter.toString()); // 可根据要求进行重写
使用enum关键字定义的枚举类实现接口
enum 实现接口,在类中实现抽象方法
package com.lee.enum_; public interface Info { void show(); } enum Season3 implements Info{ // 3. 提供当前枚举类的多个对象 SPRING(春天, 春暖花开), SUMMER(夏天, 夏日炎炎), AUTUMN(秋天, 秋高气爽), WINTER(冬天, 冰天雪地); // 1. 声明 Season 对象的属性 : private final 修饰 private final String seasonName; private final String seasonDesc; // 2. 私有化类的构造器,并给对象属性赋值 private Season3(String seasonName, String seasonDesc) { this.seasonName = seasonName; this.seasonDesc = seasonDesc; } public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } @Override public void show() { System.out.println(这是一个关于季节的枚举方法); } } // 测试枚举实现接口 class Test03 { public static void main(String[] args) { Season3 autumn = Season3.AUTUMN; autumn.show(); } }
让枚举类的对象分别去实现接口中的抽象方法
package com.lee.enum_; public interface Info2 { void showDetails(); } enum Season4 implements Info2{ // 3. 提供当前枚举类的多个对象 SPRING(春天, 春暖花开) { @Override public void showDetails() { System.out.println(这里是春天,春暖花开); } }, SUMMER(夏天, 夏日炎炎) { @Override public void showDetails() { System.out.println(这里是夏天,夏日炎炎); } }, AUTUMN(秋天, 秋高气爽) { @Override public void showDetails() { System.out.println(这里是秋天,秋高气爽); } }, WINTER(冬天, 冰天雪地) { @Override public void showDetails() { System.out.println(这里是冬天,冰天雪地); } }; // 1. 声明 Season 对象的属性 : private final 修饰 private final String seasonName; private final String seasonDesc; // 2. 私有化类的构造器,并给对象属性赋值 private Season4(String seasonName, String seasonDesc) { this.seasonName = seasonName; this.seasonDesc = seasonDesc; } public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } } // 测试枚举实现接口 class Test04 { public static void main(String[] args) { Season4 autumn = Season4.AUTUMN; autumn.showDetails(); Season4 summer = Season4.SUMMER; summer.showDetails(); } }