基础概念
1.类和对象
类的6大关系:泛化,实现,聚合,组合,关联,依赖
面向对象3大特征:封装,继承,多态
(1)实体类、抽象类、接口
类:属性+方法+构造器
抽象类:可以有0个或多个未实现/实现的方法
接口:可以有0个或多个未实现/实现的方法,区分jdk8和jdk9使用访问控制符
在interface里面的变量都是public static final 的。所以你可以这样写:
public static final int i=10;
或则
int i=10;(可以省略掉一部分)
- jdk8: public、abstract、default、static
- public、abstract:必须未实现
- default:必须要实现(至少要方法体),可以被继承和重写
- static:必须要实现(至少要方法体),不可继承只能用接口名.方法()调用
- jdk9: public、abstract、default、static、private
- public、abstract:必须未实现
- default:必须要实现(至少要方法体),可以被继承和重写
- static:必须要实现(至少要方法体),不可继承并且其他类中只能用接口名.方法()调用
- private:必须要实现(至少要方法体),只能在接口内使用
(2)嵌套类
外部类访问内部类,需要显示实例化内部类。内部类访问外部类,要根据规则
静态嵌套类和非静态嵌套类一样,都是在被调用时才会被加载并初始化
嵌套类的继承:
1.静态内部类:直接继承
class E extends New.Test2 {}
Test2是New类里的静态内部类2.成员内部类:需要构造函数传入外部类的引用,并调用其super
class W extends New.Test {
public W(New n) {
n.super();
}
}
Test是New的成员内部类ps:内部类的继承:可以是内部类继承内部类,内部类继承外部类等等,但是注意内部类的访问权限问题(private,protected,public)
①静态嵌套类
用static修饰的成员内部类,只能访问外部类的静态变量、方法,能用4种访问控制符修饰,类的内部能用static和final
public class StaticNestedClass {
// 私有局部
private int i = 0;
// 静态
public static int j = 0;
// 不变值
private final int k = 0;
// static final
private static final int m = 0;
// 静态嵌套内,这里不是innerclass,可以直接new出来
public static class PublicNestedClass {
private void test1() {
// System.out.println(i); 非innerClass不能访问enclosing类的非static属性
System.out.println(j);
System.out.println(m);
// System.out.println(k); 非innerClass不能访问enclosing类的非static属性
}
// 可以定义static方法
private static void test2() {
}
}
// 静态嵌套内,这里不是innerclass,由于是私有的,不可以直接new出来
private static class PrivateNestedClass {
}
}
下面的例子演示了static Nested class的创建
public class TestClass {
public static void main(String[] args) {
//任何地方都可以创建
StaticNestedClass.PublicNestedClass publicNestedClass = new StaticNestedClass.PublicNestedClass();
//可以在同一package下创建
StaticNestedClass.DefaultNestedClass defaultNestedClass = new StaticNestedClass.DefaultNestedClass();
//编译错误,无法访问内部内
//StaticNestedClass.PrivateNestedClass privateNestedClass = new StaticNestedClass.PrivateNestedClass();
}
}
②非静态嵌套类(内部类)
-
成员内部类:能访问外部类的所有属性和方法1,能用4种访问控制符修饰,类的内部能用final和2,当调用内部类的构造器的时候,会把当前创建的内部类对象实例中持有的外部对象引用赋值为当前创建内部类的外部类实例。
public class MemberInnerClass { // 私有局部 public int i = 0; // 静态 private static int j = 0; // 不变值 private final int k = 0; // static final private static final int m = 0; public class PublicMemberInnerClass { // enclosing Class的属性都可以访问 public void test() { System.out.println(i); System.out.println(j); System.out.println(m); System.out.println(k); } public MemberInnerClass getOutterClass() { return MemberInnerClass.this; } // 这里会报错,不允许定义static方法 // private static final void test(); } // 私有的innerclass 外部不能访问 private class PrivateMemberInnerClass { } // 公开局部类,外部可以访问和创建,但是只能通过OutterClass实例创建 class DefaultMemberInnerClass { public MemberInnerClass getOutterClass() { return MemberInnerClass.this; } } }
下面例子演示了成员内部类的创建
public class TestClass { public static void main(String[] args) { // 任何地方都可以创建 MemberInnerClass t = new MemberInnerClass(); // 可以创建,pmic里面保存对t的引用 MemberInnerClass.PublicMemberInnerClass pmic = t.new PublicMemberInnerClass(); // 可以在同一package下创建,dmic保存对t的引用 MemberInnerClass.DefaultMemberInnerClass dmic = t.new DefaultMemberInnerClass(); // 编译错误,无法访问内部内 // MemberInnerClass.PrivateMemberInnerClass pmic = t.new // PrivateMemberInnerClass(); // 下面验证一下outterClass是同一个对象 System.out.println(pmic.getOutterClass() == t); // true System.out.println(dmic.getOutterClass() == t); // true } }
-
局部内部类:可以访问方法里面定义的不变的变量,不可以使用访问控制符修饰,类的内部能用final和常量
- 静态方法里:可以访问外部类静态变量,方法(Enclosing.xxx)
- 非静态方法里:可以访问外部类非静态变量,方法(Enclosing.this.xxx)
public class EnclosingClass { public static void main(String[] args) { new Man().getWoman(); } } class People{ public People() { System.out.println("woman"); } } class Man{ public Man(){ } public People getWoman(){ class Woman extends People{ //局部内部类 int age =0; } return new Woman(); } }
-
匿名内部类:分为成员匿名内部类和局部匿名内部类
(3)枚举
枚举类的作用相当于替换一个都是常量的类
①枚举类的定义
- 一个枚举类可以包含:枚举值,变量,方法,构造函数
- 枚举值全大写,有多少个枚举值就有多少个单实例对象
- 构造函数只能是default或者private
package com.newland.paas.paasservice.clumgr.enums;
import java.util.HashMap;
import java.util.Map;
/**
* @author 金志敏
* @date 2020/5/23 16:50
*/
public enum CluPropsEnum {
NETWORK_STRATEGY("networkStrategy", "networkStrategy"),
SCALE_MASTER("scaleMaster", "scaleMaster"),
GPU_ADMISSION("gpuAdmission", "gpuAdmission"),
POD_NETWORK_SEGMENT("podNetworkSegment", "podNetworkSegment");
private String code;
private String name;
CluPropsEnum(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
/**
* 获取所有key、value,用于前端翻译
*
* @return
*/
public static Map<String, String> toMap() {
Map<String, String> values = new HashMap<>(CluPropsEnum.values().length);
for (CluPropsEnum item : CluPropsEnum.values()) {
values.put(item.getCode(), item.getName());
}
return values;
}
}
②枚举类实现原理:继承Enum类,无法被继承
//使用关键字enum定义枚举类型并编译后,编译器会自动帮助我们生成一个与枚举相关的类。我们再来看看反编译Day.class文件:
//反编译Day.class
final class Day extends Enum
{
//编译器为我们添加的静态的values()方法
public static Day[] values()
{
return (Day[])$VALUES.clone();
}
//编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法。s代表枚举名
public static Day valueOf(String s)
{
return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
}
//私有构造函数
private Day(String s, int i)
{
super(s, i);
}
//前面定义的7种枚举实例
public static final Day MONDAY;
public static final Day TUESDAY;
public static final Day WEDNESDAY;
public static final Day THURSDAY;
public static final Day FRIDAY;
public static final Day SATURDAY;
public static final Day SUNDAY;
private static final Day $VALUES[];
static
{
//实例化枚举实例
MONDAY = new Day("MONDAY", 0);
TUESDAY = new Day("TUESDAY", 1);
WEDNESDAY = new Day("WEDNESDAY", 2);
THURSDAY = new Day("THURSDAY", 3);
FRIDAY = new Day("FRIDAY", 4);
SATURDAY = new Day("SATURDAY", 5);
SUNDAY = new Day("SUNDAY", 6);
$VALUES = (new Day[] {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
}
从反编译的代码可以看出编译器确实帮助我们生成了一个Day类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类。注意编译器还为我们生成了两个静态方法,分别是values()和 valueOf(),稍后会分析它们的用法。到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;
③枚举类常用方法
- 枚举对象
返回类型 | 方法名称 | 方法说明 |
---|---|---|
T[] | values() | 返回枚举类的所有实例对象 |
T | values(String name) | 返回一个指定枚举名的枚举。name是枚举名 |
String | name()/toString() | 返回此枚举常量的名称,在其枚举声明中对其进行声明 |
int | ordinal() | 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零) |
- Class对象与枚举
返回类型 | 方法名称 | 方法说明 |
---|---|---|
T[] | getEnumConstants() | 返回该枚举类型的所有元素,如果Class对象不是枚举类型则返回null。 |
boolean | isEnum() | 当且仅当该类声明为源代码中的枚举时返回 true |
④枚举类与接口,switch
-
与接口:
public enum Meal{ APPETIZER(Food.Appetizer.class), MAINCOURSE(Food.MainCourse.class), DESSERT(Food.Dessert.class), COFFEE(Food.Coffee.class); private Food[] values; private Meal(Class<? extends Food> kind) { //通过class对象获取枚举实例 values = kind.getEnumConstants(); } public interface Food { enum Appetizer implements Food { SALAD, SOUP, SPRING_ROLLS; } enum MainCourse implements Food { LASAGNE, BURRITO, PAD_THAI, LENTILS, HUMMOUS, VINDALOO; } enum Dessert implements Food { TIRAMISU, GELATO, BLACK_FOREST_CAKE, FRUIT, CREME_CARAMEL; } enum Coffee implements Food { BLACK_COFFEE, DECAF_COFFEE, ESPRESSO, LATTE, CAPPUCCINO, TEA, HERB_TEA; } } }
-
与switch:
使用switch进行条件判断时,支持byte、char、short、int,字符串,枚举
enum Color {GREEN,RED,BLUE} public class EnumDemo4 { public static void printName(Color color){ switch (color){ case BLUE: //无需使用Color进行引用 System.out.println("蓝色"); break; case RED: System.out.println("红色"); break; case GREEN: System.out.println("绿色"); break; } } public static void main(String[] args){ printName(Color.BLUE); printName(Color.RED); printName(Color.GREEN); //蓝色 //红色 //绿色 } }
(4)注解
①java预定义元注解
-
@Retention(RetentionPolicy.xxx):表示保留
- RetentionPolicy.SOURCE:保留到.java
- RetentionPolicy.CLASS:保留到.class,不会被JVM加载
- RetentionPolicy.RUNTIME:保留到.class,会被JVM加载
-
@Target(ElementType[]):表示作用范围
- ElementType.Type:只能作用在类
- ElementType.Field:只能作用在成员变量
- ElementType.Method:只能作用在方法
- ElementType.Constructor:只能作用在构造函数
- ElementType.Paramter:只能作用在方法参数
-
@Inherited:表示可以被继承。但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解
@Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DocumentA { } @DocumentA class A{ } class B extends A{ }
-
@Repeatable:表示注解可以被重复使用,需要搭配容器注解
//Java8前无法这样使用 @FilterPath("/web/update") @FilterPath("/web/add") public class A {} //使用Java8新增@Repeatable原注解 @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(FilterPaths.class)//参数指明接收的注解class public @interface FilterPath { String value(); } //FilterPaths为容器注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface FilterPaths { FilterPath[] value(); } //使用案例 @FilterPath("/web/update") @FilterPath("/web/add") @FilterPath("/web/delete") class AA{ }
②java自定义注解:@interface和注解属性
可以声明一个String类型的name元素,其默认值为空字符,但是必须注意到对应任何元素的声明应采用方法的声明方式,同时可选择使用default提供默认值
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Menu {
String name() default "";
String[] taste() default {};
}
//String[] strs = {"1","2","3"}
@Menu(name = "fish",taste = {"fanqie","黑椒"})
class Person{
}
③java注解相关方法(注解与反射机制)
反射对象调用
返回值 | 方法名称 | 说明 |
---|---|---|
< A extends Annotation > | getAnnotation(Class annotationClass) | 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。 |
Annotation[] | getAnnotations() | 返回此元素上存在的所有注解,包括从父类继承的 |
boolean | isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。 |
Annotation[] | getDeclaredAnnotations() | 返回直接存在于此元素上的所有注解,注意,不包括父类的注解, |
(5)类的初始化和实例化过程
⭐以下情况会触发类的加载
- 遇到new,(getstatic,putstatic,invokestatic)=》调用静态属性和方法。这4个指令时
- 调用java.lang.refect反射时
- 主启动类(main函数所在的类)自动初始化
- 初始化子类时发现父类未初始化,先初始化父类
⭐以下情况不会触发类的加载
https://blog.csdn.net/w893932747/article/details/89067482
【注:final比static更早初始化,类初始化(加载)只是类生命周期的一个阶段】
①类的初始化:调用方法,执行静态域(变量,代码块),按顺序从上到下执行,只执行一次
②类的实例化(对象的实例化):
- 调用方法,有几个构造器就有几个()方法
- super();(父类实例化)
- 非静态变量和非静态代码块,按顺序从上到下执行,只执行一次
- 构造器(super()之后的代码段)
2.继承、实现和多态
(1)继承和实现
①继承
- 单根继承,一个类只能继承一个父类
- 继承除了private和构造器的所有(包括static修饰的),子类可以使用父类,父类的父类的除了private、构造器的所有
- 关于构造器:
- 子类构造器第一行必须为super()
- 如果父类存在有参构造器且没有显式声明空参构造器,则子类也必须有和父类相同形参列表的有参构造器
- 因为一个类默认有空参构造器,如果定义了一个有参构造器而没有显式声明空参构造器,则空参构造器消失
- 父类若没有空参构造器,原本子类空参构造器第一行为super(),则子类空参构造器也消失
②实现
- 一个实体类可以继承一个实体类/抽象类,实现多个接口
- 一个抽象类可以继承一个实体类/抽象类,实现多个接口
- 一个接口可以继承多个接口
(2)多态
①概念
- 继承/实现关系
- 要有方法的重写
- 父类引用指向子类对象
②关于多态下方法的调用
- 子类实例化时,会先去实例化父类
- 父类引用只能调用本类有的,而不能调用子类有的。子类引用都可以
- 非静态看右边,静态看左边
- 编译看左边,运行看右边
public class A{
static void func_a_static(){};
void func_a(){
System.out.println("A class");
}
//测试
public static void main(String[] args) {
A a = new B();
B b = new B();
a.func_a();//调用子类B类的方法
//a.func_b();无法调用
b.func_a();//调用子类B类的方法
b.func_b();
}
}
class B extends A {
static void func_b_static(){};
void func_a(){
System.out.println("B class");
}
void func_b(){
}
}
3.重载和重写
(1)重载
- 方法名相同
- 形参列表不同(个数,类型,顺序),返回值类型可以相同可以不同
- 访问权限无所谓相不相同
(2)重写
- 方法名相同,形参列表相同,返回值类型相同
- 方法体不同
- 访问权限要大于被重写方法的权限
4.static和final
(1)static
因为static方法是没有this的,也就是编译器不会像对普通的方法一样偷偷传个对象的引用this给它。可以通过在static方法中new对象通过引用来调用方法或者说是通过传对象的引用作为参数来调用非static的方法。
- 类:作用在静态嵌套类(不能作用在外部类)
- 变量:共享,拿类名调用
- 方法:共享,拿类名调用
(2)final
- 类:不能被继承(不能作用在抽象类和接口)
- 变量:不能被重赋值
- 方法:不能被重写
5.基本类型和引用类型
(1)基本类型
- byte:1字节8位。10进制的[-27,27)。即-128-127。取不到8(其它基本类型也一样,取不到x)
- boolean:false(0)/true(1)
- char:2字节16位(在UTF-8编码中,一个中文字符等于三个字节,一个中文标点符号占三个字节;一个英文字符等于一个字节,一个英文标点占一个字节;一个数字符号等于一个字节。因此char不能用于表示中文字符)
- short:2字节16位
- int:4字节32位(2的31次方=2147483648,即数字的10位)
- long:8字节64位(数字的19位)
- float:4字节16位,f后缀
- double:8字节32位,d后缀
①缓冲池
-
概念:基本类型的包装类会自带一个缓冲池,在缓冲池范围内的值调用valueOf()方法获取时会直接拿缓冲池中的对象
-
示例:
Integer x = new Integer(123); Integer y = new Integer(123); System.out.println(x == y); // false Integer z = Integer.valueOf(123);//拿缓冲池中的对象 Integer k = Integer.valueOf(123); Integer m = 123;//自动装箱,调用Integer.valueOf()方法 System.out.println(z == k); // true System.out.println(z == m); // true
-
基本类型对应的包装类型自带缓冲池如下
- boolean values true and false
- byte all byte values
- short、int、long values between -128 and 127
- char in the range \u0000 to \u007F
③自动拆装箱
1.实现原理:
- 自动装箱是通过调用包装类的valueOf方法实现
- 自动拆箱是通过调用包装类对象的xxxValue方法实现(intValue())
2.应用场景
- 集合类中都是对象类型,但是我们add(基本数据类型)也不会报错,是因为Java给我们做了自动装箱
- 比较:包装类与基本数据类型进行比较运算,*先将包装类进行拆箱成基本数据类型,然后比较
- 运算:对两个包装类型进行运算,会将包装类型自动拆箱为基本类型进行
3.注意事项
- 包装对象之间的数值比较不能简单的使用==,除了特殊情况(如Integer的-128~127),其他比较都需要使用equals比较
- 如果包装类对象为NULL,那么自动拆箱就可能会抛出NPE异常
(2)引用类型
①数组
//声明和分配内存在一起
int[] a = {1,2,3};
//声明和分配内存在一起
int[] b = new int[]{1,2,3};
//声明和分配内存分开。基本类型默认值为0,引用类型默认值为null(如Integer)
int[] c = new int[3];
c[0] = 1;
c[1] = 2;
c[2] = 3;
values true and false
- byte all byte values
- short、int、long values between -128 and 127
- char in the range \u0000 to \u007F
③自动拆装箱
1.实现原理:
- 自动装箱是通过调用包装类的valueOf方法实现
- 自动拆箱是通过调用包装类对象的xxxValue方法实现(intValue())
2.应用场景
- 集合类中都是对象类型,但是我们add(基本数据类型)也不会报错,是因为Java给我们做了自动装箱
- 比较:包装类与基本数据类型进行比较运算,*先将包装类进行拆箱成基本数据类型,然后比较
- 运算:对两个包装类型进行运算,会将包装类型自动拆箱为基本类型进行
3.注意事项
- 包装对象之间的数值比较不能简单的使用==,除了特殊情况(如Integer的-128~127),其他比较都需要使用equals比较
- 如果包装类对象为NULL,那么自动拆箱就可能会抛出NPE异常
(2)引用类型
①数组
//声明和分配内存在一起
int[] a = {1,2,3};
//声明和分配内存在一起
int[] b = new int[]{1,2,3};
//声明和分配内存分开。基本类型默认值为0,引用类型默认值为null(如Integer)
int[] c = new int[3];
c[0] = 1;
c[1] = 2;
c[2] = 3;