1. 内部类
概念:
- 我们可以将一个类写在另一个类中,称为内部类写法,内部类提供了更好的封装,只能让它的外部类直接访问,不允许同一个包中的其他类直接访问。
- 一般内部类在只为外部类提供服务的情况下优先使用。
- 内部类也需要使用public等权限修饰符类界定允许访问的范围。
1.1 成员内部类
概念:
- 成员内部类特性:
- 内部类可以访问外部类的成员(包括私有成员)。
- 外部类不可以访问内部类的任何成员。
- 成员内部类不能有静态方法、静态属性或静态块。
- 成员内部类访问外部类成员:
- 方法1:在内部类中使用外部类实例访问。
- 方法2:在内部类中使用
外部类名.this.属性名格式进行访问。
- 获取成员内部类实例: 先获取外部类实例,然后使用
外部类.new 内部类()的格式来获取内部类实例。- 内部类实例可以使用内部类类型接收,但是需要导包。
- 内部类实例可以使用
外部类.内部类的格式类型接收,无需导包。
内部类产生的class文件的格式是:
外部类$内部类.class,如上面的案例,产生的class文件为:Outer$Inner.class。
源码: /javase-oop/
- src:
c.y.innerclass.MemberInnerClassTest
/**
* @author yap
*/
public class MemberInnerClassTest {
@Test
public void innerClass() {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.innerMethod();
}
}
class Outer {
private String field = "field-outer";
class Inner {
private String field = "field-inner";
public void innerMethod() {
System.out.println(field);
System.out.println(new Outer().field);
System.out.println(Outer.this.field);
}
}
}
1.2 静态内部类
概念:
- 静态内部类特性:
- 内部类可以访问外部类的成员(包括私有成员)。
- 外部类不可以访问内部类的任何成员。
- 静态内部类可以拥有静态属性、静态块和静态方法。
- 静态内部类访问外部类成员: 允许在静态内部类中使用外部类实例访问,但不允许使用
外部类名.this来访问。 - 获取静态内部类实例: 先获取外部类实例,然后使用
外部类.内部类()的格式来获取内部类实例(无需new静态内部类)。- 内部类实例可以使用内部类类型接收,但是需要导包。
- 内部类实例可以使用
外部类.内部类的格式类型接收,无需导包。
源码: /javase-oop/
- src:
c.y.innerclass.StaticInnerClassTest
/**
* @author yap
*/
public class StaticInnerClassTest {
@Test
public void staticInnerClass() {
StaticOuter.StaticInner staticInner = new StaticOuter.StaticInner();
staticInner.method();
StaticOuter.StaticInner.staticMethod();
}
}
class StaticOuter {
private int field = 1;
private static int staticField = 2;
static class StaticInner {
private int field = 3;
private static int staticField = 4;
public void method() {
System.out.println(field);
System.out.println(staticField);
System.out.println(new StaticOuter().field);
System.out.println(StaticOuter.staticField);
// System.out.println(Outer.this.field);
}
public static void staticMethod() {
System.out.println(staticField);
System.out.println(new StaticOuter().field);
System.out.println(StaticOuter.staticField);
// System.out.println(field);
// System.out.println(Outer.this.field);
}
}
}
1.3 匿名内部类
概念:
- 匿名内部类适合那种只需要使用一次的类,比如线程的创建等。
- 匿名内部类格式为:
new 父类构造/接口(){}。 - 匿名内部类一般用于快速实现抽象类和内部接口。
源码: /javase-oop/
- src:
c.y.innerclass.AnonymousInnerClassTest
/**
* @author yap
*/
public class AnonymousInnerClassTest {
@Test
public void baseServiceByInnerClass() {
new BaseService() {
@Override
public void create() {
System.out.println("添加...");
}
}.create();
}
@Test
public void userServiceByInnerClass() {
new UserService() {
@Override
public void create() {
System.out.println("添加...");
}
}.create();
}
}
abstract class BaseService {
/**
* 添加数据
*/
abstract void create();
}
interface UserService {
/**
* 添加数据
*/
void create();
}
1.4 Lambda表达式
概念:
- Lambda表达式是为了简化函数式接口的实现过程而存在的,它只为简化代码,不能提高效率。
- 函数式接口:如果一个接口中只有一个抽象方法,那么它就可以称为函数式接口,函数式接口中允许存在静态方法和default方法。
1.4.1 表达式结构
概念:
- 主要结构:
(参数列表) -> {方法体},其中->代表传递。 - 如果参数只有一个,可以省略括号,没有参数的时候不能省略。
- 如果过程中只有一行代码可以省略,如果过程中只有一个return也可以省略。
源码: /javase-oop/
- src:
c.y.innerclass.LambdaInnerClassTest
/**
* @author yap
*/
public class LambdaInnerClassTest {
@Test
public void lambda() {
AdditionService additionService = (numA, numB) -> numA + numB;
System.out.println(additionService.add(10, 20));
}
}
interface AdditionService {
/**
* 两个int数相加的方法
*
* @param numA 加法运算的一个数字
* @param numB 加法运算的另一个数字
* @return 返回两个数相加的结果
*/
int add(int numA, int numB);
}
1.4.1 表达式返回值
概念:
- Lambda表达式用来替代匿名内部类实现接口,得到接口实现类的对象,这也就意味着Lambda表达式具有自己的返回值类型。
- Lambda表达式的返回值类型,就是其实现的接口的类型,且该接口必须是函数式接口。
- 为了方便Lambda表达式的使用,Java8中提供了4类新的核心接口配合Lambda表达式的功能,它们都定义在
java.util.function包下。- 消费型接口Consumer,内置
accpet(),只有参数,没有返回值。 - 供给型接口Supplier,内置
get(),只有返回值,没有参数。 - 函数型接口Function,内置
apply(),既有返回值,又有参数。 - 断言型接口Predicate,内置
test(),有参数,且返回值固定为boolean类型。
- 消费型接口Consumer,内置
源码: /javase-oop/
- src:
c.y.innerclass.LambdaReturnValueTest
/**
* @author yap
*/
public class LambdaReturnValueTest {
@Test
public void returnValue(){
Consumer consumer = p1 -> System.out.println("hello");
Supplier supplier = () -> 100;
Function function = p1 -> 100;
Predicate predicate = p1 -> true;
}
}
2. 枚举类
概念:
enum全称enumeration,是JDK1.5中引入的新特性,存放在java.lang包中,它的语法结构尽管和普通class的语法不一样,但是经过编译器编译之后产生的仍然是一个class文件。- 所有的
enum都默认继承了抽象类java.lang.Enum<E>,即不能再继承其他类。
2.1 枚举类定义
概念:
- 创建枚举类时,关键字要使用
enum替换class。 - 枚举类中的每个属性都将映射到
Enum类的唯一构造器中:protected Enum(String name, int ordinal)name:枚举属性的名称,是一个字符串。ordinal:枚举属性被创建的顺序,从0开始。
- 这个构造器不能被显示调用,而是编译器根据枚举属性自动调用的。
- 枚举类中的属性建议使用大写。
- 允许在枚举类中,像普通类那样自定义属性和方法,但是必须写在枚举常量的下面。
枚举类名.枚举常量可以返回一个该枚举类的枚举对象。
源码: /javase-oop/
- src:
c.y.enumclass.EnumTest
/**
* @author yap
*/
public class EnumTest {
@Test
public void week(){
Week mon = Week.MON;
System.out.println(mon);
}
}
enum Week{
/**星期一到星期日*/
MON, TUE, WED, THU, FRI, SAT, SUN;
/*
// 上面的枚举属性相当于调用了7次Enum类的构造器代码:
new Enum<Week>("MON", 0);
new Enum<Week>("TUE", 1);
new Enum<Week>("WED", 2);
new Enum<Week>("THU", 3);
new Enum<Week>("FRI", 4);
new Enum<Week>("SAT", 5);
new Enum<Week>("SUN", 6);
*/
}
2.2 自定义构造器
概念: 枚举类可以定义自己的构造器来覆盖 Enum 的构造器,但默认且只能是private修饰的,一样不可以被调用。
源码: /javase-oop/
- src:
c.y.enumclass.CustomEnumTest
/**
* @author yap
*/
public class CustomEnumTest {
@Test
public void rgbColorTest() {
System.out.println(RgbColor.RED.getRgb());
}
}
enum RgbColor {
/**
* RGB三原色
*/
RED(1), GREEN(2), BLUE(3);
private int rgbCode;
RgbColor(int rgbCode) {
this.rgbCode = rgbCode;
}
public int getRgb() {
return rgbCode;
}
}
枚举类中的方法需要使用某个枚举属性来调用。
2.3 枚举类常用API
概念: 枚举类常用的API方法如下:
枚举类名.values()- 获取枚举类中所有的枚举属性组成的枚举数组。
- 在我们编写自定义的enum时,是没有
values()的,只有在编译java文件时,javac才会自动帮助我们生成这个方法。
枚举类名.枚举常量.toString()- 返回该枚举对象的全部信息(名,序号等)的字符串。
- 可以在枚举属性后添加匿名内部类来重写toString:
MON{@Override public String toString(){ return "星期一";}}
枚举类名.枚举常量.name()- 返回该枚举对象的名字。
枚举类名.枚举常量.ordinal()- 返回枚举对象从0开始的序号。
枚举类名.枚举常量.compareTo(枚举类名.枚举常量)- 返回两个枚举对象的序号(ordinal)差。
源码: /javase-oop/
- src:
c.y.enumclass.EnumApiTest
/**
* @author yap
*/
public class EnumApiTest {
@Test
public void api() {
System.out.println(EnumDemo.ONE.toString());
System.out.println(EnumDemo.SIX.name());
System.out.println(EnumDemo.FOR.ordinal());
System.out.println(EnumDemo.TWO.compareTo(EnumDemo.FIV));
for (EnumDemo e : EnumDemo.values()) {
System.out.print(e.toString() + "\t");
}
}
}
enum EnumDemo{
/**1到6*/
ONE, TWO, THR, FOR, FIV, SIX;
}
2.4 switch中使用枚举
概念: switch()中可以直接使用枚举类型。
源码: /javase-oop/
- src:
c.y.enumclass.EnumInSwitchTest
/**
* @author yap
*/
public class EnumInSwitchTest {
@Test
public void enumForSwitch() {
Week dayOfWeek = Week.SAT;
switch (dayOfWeek) {
case SAT:
System.out.println("复习..");
break;
case SUN:
System.out.println("旅游..");
break;
default:
System.out.println("上课..");
break;
}
}
}
3. 注解类
概念:
- Annotation是从JDK5开始引入的新技术,注解不是注释(comment)、注解和注释一样,都可以对程序作出某种解释,但不同的是,注解可以被其它程序读取并操作。
- 注解是一个特殊的类:
- 以
public @interface 注解名{}的方式被创建。 - 以
@注解名(KV)的格式被使用。
- 以
- 注解可以修饰
package、class、method和field等,但是在同一个位置,相同注解不能重复使用。
3.1 内置注解
概念: jdk提供的默认注解,我们叫做内置注解,可以直接使用,使用方式如下:
- 注解如果只有一个参数,则可以使用
@注解名(KV)的方法来使用。 - 注解如果有多个参数,则必须使用
@注解名({KV, KV, KV...})的方法来使用,使用大括号修饰。
如果唯一的参数名是
value,则value也可以省略。
3.1.1 @Override
概念: 当子类重写了父类方法的时候,建议在重写方法上添加 @Override 来告诉javac,这个方法是一个重写方法,此时这个方法必须按照重写方法的格式来编写,否则javac将报错。
源码: /javase-oop/
- src:
c.y.annotation.OverrideTest
/**
* @author yap
*/
public class OverrideTest {
private static class OverrideDemo {
@Override
public String toString() {
return "重写了toString()";
}
}
@Test
public void studentToString() {
System.out.println(new OverrideDemo().toString());
}
}
3.1.2 @Deprecated
概念: 表示不推荐使用的方法,当某个方法被此注解标识,则代表这个方法过时,在使用它的时候,javac会为此方法上添加删除线以作警示,但是这个方法仍能正确使用,不推荐不代表不能用。
源码: /javase-oop/
- src:
c.y.annotation.DeprecatedTest
/**
* @author yap
*/
public class DeprecatedTest {
private static class DeprecatedDemo {
@Deprecated
public void method() {
System.out.println("deprecated method...");
}
}
@Test
public void studentDeprecated() {
new DeprecatedDemo().method();
}
}
3.1.3 @SuppressWarnings
概念: @SuppressWarnings 用于镇压、抑制编译器产生警告信息,常用值如下:
all:所有警告。unused:未曾使用过这个属性或方法的警告。boxing:装箱拆箱相关警告。cast:类型转换相关警告。dep-ann:使用了过时的注解的警告。deprecation:使用了过时的类或方法的警告。fallthrough:switch发生case穿透(未写break)时的警告。finally:finally块不能完成时的警告。hiding:不可见的局部变量警告。incomplete-switch:switch语句中缺少条目警告。nls:非NLS字符串文本警告。null:空分析警告。rawtypes:对类参数使用泛型时的非特定类型的警告。restriction:不鼓励或禁止引用的用法的警告。serial:可序列化的类中没有serialVersionUID属性。static-access:不正确的静态访问警告。synthetic-access:从内部类进行未优化的访问警告。unchecked:使用了未检查警告。unqualified-field-access:字段访问不合格相关的警告。
源码: /javase-oop/
- src:
c.y.annotation.SuppressWarningsTest
/**
* @author yap
*/
public class SuppressWarningsTest {
@SuppressWarnings(value = "unused")
public void warning() {
int num = 10;
}
}
3.2 元注解
概念: 元注解(meta-annotation)就是注解其他注解的注解,它负责对其他注解进行说明,Java中定义了四种元注解:
@Target:描述注解的使用范围,即这个注解可以作用在什么地方,包上?方法上?类上?@Retention:描述注解的保留策略,即注解的生命周期,注解何时生效、何时失效?@Documented:标明这个注解会成为javadoc的一部分。@Inherited:标明这个注解可以被子类继承。
3.2.1 @Target
概念: @Target描述注解的使用范围,注解中有一个参数value,值是 ElementType[] 枚举数组类型,ElementType枚举属性如下:
ElementType.TYPE:表示该注解可以定义在接口、类和枚举类上。ElementType.FIELD:表示该注解可以定义在字段、枚举常量上。ElementType.METHOD:表示该注解可以定义在方法上。ElementType.PARAMETER:表示该注解可以定义在方法参数上。ElementType.CONSTRUCTOR:表示该注解可以定义在构造器上。ElementType.LOCAL_VARIABLE:表示该注解可以定义在局部变量上。ElementType.ANNOTATION_TYPE:表示该注解可以定义在注解上。ElementType.PACKAGE:表示该注解可以定义在包上。
3.2.2 @Retention
概念: 描述注解的生命周期,注解中有一个参数value,值是RetentionPolicy[]枚举数组类型,RetentionPolicy枚举属性如下:
RetentionPolicy.SOURCE:表示该注解只在源代码中保留。RetentionPolicy.CLASS:表示该注解在类代码中保留,但在运行时无法获得。RetentionPolicy.RUNTIME:表示该注解在整个运行期间都保留。
只有生命周期为RUNTIME的时候,该注解才能被反射到。
3.3 自定义注解
概念:
- 使用@interface自定义注解时,自动继承了
java.lang.annotation.Annotaion接口。 - 自定义注解属性的时候,要遵循以下规定:
- 方法名就是是参数名,如果只有一个参数,一般起名为value,因为使用者可以对它省略。
- 方法返回值就是参数类型。
- 可以通过default来声明参数的默认值。
- 自定义注解的时候需要使用元注解,来声明你定义的注解可以用在什么地方,以及何时有效,如:
@Target({ElementType.TYPE, ElementType.METHOD}):可以作用在类上或者方法上。@Retention(RetentionPolicy.RUNTIME):整个运行期间都能有效。
源码: /javase-oop/
- src:
c.y.annotation.CustomAnnotationTest
/**
* @author yap
*/
public class CustomAnnotationTest {
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// String name;
String name();
// int age = 18;
int age() default 18;
// String[] course = {"语文", "数学"}
String[] course() default {"语文", "数学"};
}
@MyAnnotation(name = "赵四")
private static class Student {
@MyAnnotation(name = "刘能")
public void method(){
}
}
}