概述
类:对一类事物共同点的 描述
对象:是对某一个事物的一个 个体
描述说明
静态成员: 静态方法 静态属性
普通成员: 普通方法 普通属性
类的基本概念
访问修饰符
| 访问控制修饰符 | 访问级别 | 同类 | 同包 | 子类 | 不同包 |
|---|---|---|---|---|---|
public | 公开 | ||||
protected | 受保护啊 | ||||
| 默认 | |||||
private | 私有 |
属性和方法
- 属性
- 在
java中,成员方法可以访问,静态属性,和方法,静态方法只能访问静态属性和方法
// 一个简单的类 public class BasicClass { public static String name = "lisa"; public int age = 10; public void hi(){ } public static void hello(){ } }-
属性的定义类型可以为任意类型,包含基本类型和引用类型
-
属性如果不赋值,有默认值
(String:null,int:0,double:0.0,boolean:false 以此类推)
- 在
- 方法
- 可变参数
- 可以传入多个相同的参数
- 可以参数的可以是0个或任意多个,参数可以为数组
- 可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可以参数
public class BasicClass { public void sum( int... mums){ } } BasicClass basicClass = new BasicClass() // 可以参数的可以是0个或任意多个 basicClass.sum() basicClass.sum(1, 2, 3) // 可以参数可以为数组 int[] arr ={1,2,3} basicClass.sum(arr)
作用域
-
说明
- 静态成员的作用域是整个类,普通成员的作用域是整个实现类,局部变量的作用域在代码块中
- 全局变量可以赋值,因为有默认值,局部变量必须赋值
-
注意
- 全局变量和局部变量可以重名,访问域遵循就近原则
- 全局变量生命周期和类相同,局部变量和代码块相同
构造方法
- 注意
- 构造方法遵循修饰符规则
- 构造方法没有返回值
- 构造方法是完成对象的初始化,并不是创建对象
(当调用构造方法的时候 类其实已经在堆里面存在了,调用构造方法其实只是给类赋值) - 如果一个类中没有写构造方法,编译后会默认生成一个无参的构造方法,但是如果写了就不会生成默认的无参构造方法,所以
如果我们定义了一个有参构造,又需要调用无参构造,就需显示定义一个无参构造
this 和 super
-
this
this可以用来在静态变量(静态方法)和 成员变量(成员方法)重名的时候区分它们,加了this前缀就是 成员变量(成员方法)(注意在方法内的属性访问遵循就近原则)this只能在 成员变量(成员方法)中使用- 在
构造方法中使用this只能调用构造方法,在构造方法调用this,必须放置第一条语句
// 构造方法中调用的例子 public class BasicClass { String name = "a"; BasicClass(){ // System.out.println("ad"); // 必须放在第一行 this("hai"); } BasicClass(String name){} public static void hello(){ String name = "b"; // 就近原则 System.out.println(name); // 输出:b System.out.println(this.name); // 输出:a } -
super
super可以访问遵循访问修饰符super在构造方法中和this只能出现一个,并且在第一行- 子类的成员方法可以通过
super使用父类方法 super的访问不限于父类,是按照超类中的就近原则查找
-
总结 | 区别 | this | super | | --- | --- | --- | | 访问属性 | 访问本类中的属性,如果本类没有此属性,
则从父类继续查找 | 从父类开始查找 | | 调用方法 | 访问本类中的方法,如果本类没有此方法,
则从父类继续查找 | 从父类开始查找 | | 调用构造方法 | 调用本类构造方法,必须放在构造方法首行 | 调用父类构造方法,必须放在构造方法首行 | | 意义 | 表示当前对象 | 子类中访问以父类开始的对象 |
代码块
- 语法
- 修饰符可选,要写的话也只能写
static - 代码块分为两类
静态代码块和普通代码块 - 逻辑语句可以为任何逻辑语句,
;可以忽略 - 代码块在构造方法之前执行,可以理解为是对构造方法的一种补充说明
静态代码块什么时候被加载
- 静态代码块在类加载的时候执行,并且只会在第一次加载的时候执行
- 创建子类的时候,从顶层类开始加载
- 使用静态成员的时加载
Animal dog = new Dog(),Animal类中的静态代码块也会执行- 静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态属性初始化,按照定义的顺序调用
- 静态代码块只能调用静态成员
普通代码块
- 普通代码块实例创建一次就会调用一次
- 普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,按照定义的顺序调用
- 普通代码块可以调用任意成员
- 子类实例创建的时候,从顶层类开始加载
例子如下
public class Animal {
{
System.out.println(" Animal Block");
}
public Animal() {
System.out.println(" new Animal");
}
}
public class Dog extends Animal {
{
System.out.println(" Dog Block");
}
public Dog() {
// super();
// 调用普通代码块
System.out.println("new Dog");
}
}
// 执行顺序
// Animal Block
// new Animal
// Dog Block
// new Dog
重点
创建C extends B extends A,三个对象,之心顺序如下- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 父类构造方法
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类构造方法
public class A {
static {
System.out.println("static A Block");
}
{
System.out.println("A Block");
}
public A() {
System.out.println("new A");
}
}
public class B extends A{
static {
System.out.println("static B Block");
}
{
System.out.println("B Block");
}
public B() {
System.out.println("new B");
}
}
public class C extends B{
static {
System.out.println("static C Block");
}
{
System.out.println("C Block");
}
public C() {
System.out.println("new C");
}
}
// 执行顺序
// static A Block
// static B Block
// static C Block
// A Block
// new A
// B Block
// new B
// C Block
// new C
final
- 说明
- final 是最后,最终的意思
- final 可以修饰类,属性,方法和局部变量
- 什么情况下使用 final
- 当不希望类被继承时
final public class A {} - 当不希望父亲的某些方法被子类重写时
final public class A { public final void hi(){} public static final void hi(){} } - 当不希望类的某个属性的值被修改
final public class A { public final name public static final name } - 当不希望某个局部变量被修改
final public class A { public final void hi(){ final double NUM = 0.8 } }
- 当不希望类被继承时
- 注意
- final修饰的属性又叫常量,一般用 大写下划线来命名
- final修饰的属性在定义时,必须有初始值,并且之后不能修改
( 静态属性,普通属性,构造方法,代码块) - 如果final修饰的是静态属性,则初始化的位置只能是 ,定义时、在静态代码块,不能在构造方法中赋值
- final类不能继承,但是可以实例化对象
- 如果类不是final类,但是含有final方法,则该方法不能重写,类依然可以被继承
- 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
- final不能修饰构造方法
- final 和 static 往往搭配使用,效率更高,底层编译器做了优化处理
final public class A { public static final name = 1000; static { System.out.println("A static Block"); } } System.out.println(A.nam); // 1000 ,A static Block 不会执行 - 包装类(Integer,Double,Float,Boolean等都是final),String也是final
继承
- 注意
- 如果子类想访问超类的私有属性和方法,需要超类提供公共方法来访问
- 子类必须调用父类构造方法,完成父类的初始化
- 当创建子类时,不管使用子类的那个构造方法,一定会去调用父类的构造方法,如果父类没有无参构造方法,子类指定调用父类的哪个构造方法
super必须放在构造方法的第一行super和this在构造方法中不能共存- java 中所有的类都是都是
Object的子类
public class BasicClass {} public class Sub extends BasicClass { Sub(){ // 调用父类构造方法,不写程序会自动添加 父类无参构造 super(); } /* public Sub() { this("a"); } public Sub(String name) { // 原则是在调用构造起始处开始 调用 super() super(); } */ } ```
多态
方法多态
-
Overload 重载
- 方法名必须相同
- 参数列表必须不同
(类型,个数,顺序至少一个不同) - 返回类型无要求
(返回类型不同不会构成方法的重载)
-
Override 重写
- 子类方法的
方法名,参数要和父类方法的的方法名,参数完全一样 - 子类方法的返回类型要和父类方法返回类型一样,或者是父类返回类型的子类
比如:父类返回类型是 Object,子类方法返回类型是String - 子类方法不能缩小父类方法的访问权限
private -> protected -> default -> public
- 子类方法的
-
总结
名称 发生范围 方法名 形参列表 返回类型 修饰符 Overload 本类 必须一样 类型,个数,顺序至少一个不同 无要求 无要求 Override 父子类 必须一样 相同 子类重写的方法,返回的类型和父类返回得了诶行一致,或者是其子类 子类不能缩小父类方法的访问范围
类多态
- 重点
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变的
- 编译类型看定义时,
编译类型(等号左边) = 运行类型(等号右边)
-
向上转型
- 本质:父类的引用指向子类的对象
- 语法:父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(遵循访问修饰符规则),不能调用子类中的特有成员;最终运行类型看子类的具体实现
-
向下转型
- 语法:子类类型 引用名 = new (子类类型)父类引用();
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象 (一般使用
instanceof) - 当向下转型后,可以调用子类类型中所有成员
属性多态
- 属性没有重写只之说!属性的值看编译类型
public class Base {
int count = 10
}
public class Sub extends Base {
int count = 20
}
Base base = new Sub();
System.out.println(base.count); // 10
Base sub = new Sub();
System.out.println(sub.count); // 20
内部类
一个类的内部又完整嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员(属性,方法,构造器,代码块,内部类),内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
局部内部类
- 说明
- 可以直接范根外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用
final修饰 - 作用域仅仅在定义它的方法或代码块中
- 局部内部类访问 --> 外部类的成员可以直接访问
- 外部类在方法中访问-->局部内部类的成员,需要创建对象再访问(必须在作用域内)
- 外部其其它类不能访问->局部内部类
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.普通成员,外部类名.静态成员)
class InnerClass02{} public class OuterClass { private int n1 = 100; private void m2(){}; public void m1(){ class InnerClass01 extends InnerClass02 { public void f1() { // 局部内部类访问 --> 外部类的成员可以直接访问 System.out.println(n1); m2(); } } // 外部类在方法中访问-->局部内部类的成员,需要创建对象再访问(必须在作用域内) InnerClass01 innerClass01 = new InnerClass01(); innerClass01.f1(); }; }
匿名内部类
- 重点细节
- 本质是内部类,用完就会被回收
- 该类没有名字(实际是由系统自动自动生成,下面的例子, OuterClass1 就是系统生成的)
- 匿名内部类 可以基于类,接口,抽象类创建
- 可以直接访问外部类的所有成员,包含私有
- 不能添加访问修饰符,因为它的地位是一个局部变量
- 作用域仅仅在定义它的方法或代码块中
- 匿名内部类访问 --> 外部类的成员可以直接访问
- 外部其其它类不能访问->匿名内部类
- 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.普通成员,外部类名.静态成员)
AnonymousInnerClass { public void say(){} } public class OuterClass { public void m2(){ AnonymousInnerClass anonymousInnerClass = new AnonymousInnerClass() { @Override public void say(){ System.out.println("say:"+this.getClass()); // OuterClass$1 } }; System.out.println(anonymousInnerClass.getClass()); // OuterClass$1 anonymousInnerClass.say(); // 两种访问方式 // new AnonymousInnerClass() { // @Override // public void say(){ // System.out.println("say:"+this.getClass()); // OuterClass$1 // } // }.say(); }; }
成员内部类
public class OuterClass{
public class MemberClass{
}
public void m3(){
new MemberClass();
}
}
OuterClass outerClass = new OuterClass();
outerClass.m3()
- 说明
- 成员内部类是定义在外部类的成员位置,并且没有static修饰符
- 可以直接访问外部类的所有成员,包括私有
- 可以添加任意修饰符(private , protected, default , public),因为它是一个成员
- 作用域和其它成员一样
- 成员内部类访问 --> 外部类的成员可以直接访问
- 外部类在方法中访问-->成员部类,在方法内创建方法,再访问
- 外部其他类访问成员类的三种方式
public class OuterClass{ public class MemberClass{ } public MemberClass getMemberClass(){ return new MemberClass(); } } OuterClass outerClass = new OuterClass(); // 方式一 OuterClass.MemberClass memberClass = outerClass.new MemberClass(); // 方式二 outerClass.getMemberClass(); - 如果外部类和成员内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.普通成员,外部类名.静态成员)
静态内部类
public class OuterClass{
public static class StaticClass{};
}
- 说明
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问普通成员
- 可以添加任意修饰符(private , protected, default , public),因为它是一个成员
- 作用域和其他静态成员一样
- 可以访问外部类的所有静态成员
- 外部类要访问静态内部类,创建对象,再访问
public class OuterClass{ public static class StaticClass{}; public StaticClass getStaticClass(){ return new StaticClass(); } public static StaticClass _getStaticClass(){ return new StaticClass(); } } // 方式一 OuterClass outerClass = new OuterClass(); outerClass.getStaticClass() // 方式二 OuterClass.StaticClass staticClass = new OuterClass.StaticClass(); // 方式三 OuterClass._getStaticClass() - 如果外部类和静态内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.静态成员)
抽象类
- 说明
- 解决父类方法不确定性的问题
- 用abstract关键字来修饰一个类,抽象类自己不能创建实例
public abstract class Animal{} - 用abstract关键字类修饰一个方法时,这个方法就是抽象方法,没有方法体,抽象方法所在类必须是抽象类
public abstract class Animal{ public abstract void say(); } - 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
- 抽象类可以有任何成员
(抽象类还是类,普通类有什么抽象类就有什么) - 抽象方法不能有主体
- 如果一个类继承了抽象类,则必须实现抽象类的所有抽象方法,除非自己也声明为 abstract 类
- 抽象方法不能使用 private,final ,static 来修饰,因为这些关键字和重写相违背
接口
- 说明
- 接口有,抽象方法(无方法体),静态方法(有方法体), 默认方法(有方法体)
- 在jdk7之前接口里面所有的方法都没有方法体,jdk8之后接口可以有默认方法,也就是说方法可以有具体实现(包含静态方法)
public interface IA { String name = "hi"; Integer age = 1; default public void hi1(){ System.out.println("hi1"); } public static void hi2(){ System.out.println("hi2"); } public static void hi3(); } public class A implements IA{ public static void hi3(){}; } System.out.println(A.name); // hi System.out.println(A.age); // 1 IA.hi2(); // hi2 A a = new A(); a.hi1(); // hi1 - 注意
- 接口不能被实例化
- 接口中的所有方法都是
public方法,接口中抽象方法,可以不用abstract修饰符,编译后会自动加上,(在子类中缩小修饰符的范围可以验证)// 实际上是如下 public interface IA { abstract public void hi3(); } - 一个普通类实现接口,就必须将该接口的所有方法都是实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类可以实现多个接口
- 接口中的属性只能是
final的, 而且只能是public static final修饰符public interface IA { String name = "hi"; // 实际如下,必须初始化 // public static final String name = "hi"; } - 一个接口不能继承其它的类,但是可以继承多个别的接口
interface IA extends IB,IC {} - 接口的修饰符 只能是public 和默认,这和类的修饰符一样
- 接口可以多态,也就是说可以接口转型
枚举类
-
自定义枚举类
public class Season { private String key; private String value; public Season(String key, String value) { this.key = key; this.value = value; } public String getKey() { return key; } public String getValue() { return value; } @Override public String toString() { return "Season{" + "key='" + key + ''' + ", value='" + value + ''' + '}'; } public final static Season A = new Season("keyA","valueA"); public final static Season B = new Season("keyB","valueB"); public final static Season C = new Season("keyC","valueC"); public final static Season D = new Season("keyD","valueD"); } -
enum 关键字枚举类
public enum EnumSeason { A("keyA","valueA"), B("keyB","valueB"), C("keyC","valueC"), D("keyd","valueD"), E; private String key; private String value; private EnumSeason() { } private EnumSeason(String key, String value) { this.key = key; this.value = value; } public String getKey() { return key; } public String getValue() { return value; } @Override public String toString() { return "EnumSeason{" + "key='" + key + ''' + ", value='" + value + ''' + '}'; } }
- 注意
- 当我们使用 enum 关键字创建枚举类的时候,会默认继承 Enum类
- 传统的
public final static Season A = new Season("keyA","valueA");简化成A("keyA","valueA") - 如果使用无参构造器创建枚举类,则实参列表和小括号都能省略,看上方案例
E - 当枚举类都好分割,不许放在首行
- 枚举不能继承其他类
- 枚举类和普通类一样可以实现接口支持
implements