static
静态,可以修饰成员变量、成员方法。
成员变量
成员变量按照有无static修饰,分为两种:
类变量:有static修饰,属于类,在计算机里只有一份,被类的全部对象共享。
访问方式:类名.类变量 (推荐),对象.类变量(不推荐)。
实例变量(对象的变量):无static修饰,属于每个对象的。
访问方式:对象.实例变量。

成员变量的执行原理

类变量的应用场景
如果某个数据只需要一份,且希望能够被共享(访问、修改),则将该数据定义成类变量。
1 2 3 4 5 6 7 8 9
| public class User { public static int count; public User(){ count++; } }
|
1 2 3 4 5 6 7 8
| public class Test { public static void main(String[] args) { new User(); new User();
System.out.println(User.count); } }
|
成员方法
类方法(静态方法):有static修饰的成员方法,属于类。类方法只能访问类变量,不能访问实例变量。
访问方式:类名.类方法 (推荐),对象名.类方法(不推荐)
实例方法(对象的方法):无static修饰的成员方法,属于对象。
访问方式:对象.实例方法
成员方法的执行原理

注意:
main方法是类方法。调用java Test执行程序时,实际执行的是Test.main(),所以main方法能直接跑起来。
类方法的应用场景:设计工具类
工具类优点:
工具类中的方法都是类方法,每个类方法都是用来完成一个功能的,提高代码的复用性。
实例方法需要创建对象来调用,此时对象只是为了调用方法,对象占内存,这样会浪费内存。使用类方法,直接用类名调用即可,调用方便,节省内存。
注意:工具类不需要创建对象, 建议将工具类的构造器私有化。
注意:
1.类方法中可以直接访问类的成员,不可以直接访问实例成员。
2.实例方法中既可以直接访问类成员,也可以直接访问实例成员。
3.实例方法中可以出现this关键字,类方法中不可以出现this关键字的。
代码块
代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)。
静态代码块:
格式:static { }
特点:类加载时自动执行,由于类只会加载一次,所以静态代码块只会执行一次。
作用:完成类的初始化,例如:对类变量的初始化赋值。
实例代码块:
格式:{ }
特点:每次创建对象时执行实例代码块,并在构造器前执行。
作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值。
设计模式
设计模式就是具体问题的最优解决方案。
单例设计模式:确保一个类只有一个对象。
分类:
饿汉式单例:拿对象时,对象早就创建好了。
写法:
1 2 3
| 1.把类的构造器私有。 2.定义一个类变量记住类的一个对象。 3.定义一个类方法,返回对象。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class A {
private static A a = new A();
private A(){}
public static A getInstance(){ return a; } }
|
应用场景:任务管理器对象、获取运行时对象。可以避免浪费内存。
懒汉式单例:拿对象时,才开始创建对象。(延迟加载对象)
写法:
1 2 3
| 1.把类的构造器私有。 2.定义一个类变量用于存储对象。 3.提供一个类方法,保证返回的是同一个对象。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class B { public static B b;
private B(){}
public static B getInstance(){ if(b == null){ b = new B(); } return b; } }
|
继承extends
继承就是用extends关键字,让一个类和另一个类建立起一种父子关系。
子类能继承父类的非私有成员(成员变量、成员方法)。
子类的对象是由子类、父类共同完成的。
1
| public class B extends A{}
|
继承的执行原理

权限修饰符
权限修饰符:限制类中的成员(成员变量、成员方法、构造器、代码块…)能够被访问的范围。

单继承、Object类
Java是单继承的:一个类只能继承一个直接父类;Java中的类不支持多继承,但是支持多层继承。
Object类是Java中所有类的祖宗。
方法重写
子类重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
重写后方法的访问,Java会遵循就近原则 。
注意事项
1.使用@Override注解可以指定java编译器,检查方法重写的格式是否正确。
2.子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限( public > protected > 缺省 )。
3.重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
4.私有方法、静态方法不能被重写,如果重写会报错的。
5.子类重写Object类的toString()方法,以便返回对象的内容。
子类访问其他成员(成员变量、成员方法)依照就近原则
1.先子类局部范围找。
2.然后子类成员范围找。
3.然后父类成员范围找,如果父类范围还没有找到则报错。
注意:
如果子父类中,出现了重名的成员,会优先使用子类的。可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法。
1 2 3 4 5 6 7 8
| public class A { String name = "父类name";
public void print() { System.out.println("父类的print方法"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class B extends A{ String name = "子类name";
public void showName(String name){ System.out.println(name); System.out.println(this.name); System.out.println(super.name); }
@Override public void print(){ System.out.println("子类的print方法"); }
public void showMethod(){ print(); super.print(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Test { public static void main(String[] args) { B b = new B(); b.showName("外部"); b.showMethod(); } }
|
子类构造器
子类的全部构造器,都会先调用父类的构造器,再执行自己。
注意:
1.默认情况下,子类全部构造器的第一行代码都是super()(写不写都有) ,它先会调用父类的无参数构造器。
2.如果父类没有无参数构造器,则必须在子类构造器的第一行手写super(….),指定去调用父类的有参数构造器。

this()调用兄弟构造器
任意类的构造器中,是可以通过this(…) 去调用该类的其他构造器的。
注意:
this(…) 、super(…) 都只能放在构造器的第一行,因此,有了this(…)就不能写super(…)了,反之亦然。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Student { String name; int age; String gender;
public Student() { this("no one", 0, "no gender"); }
public Student(String name, int age){ this(name,age,"女"); }
public Student(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; }
@Override public String toString() { return "姓名:"+name+", 年龄:"+age+", 性别:"+gender; } }
|
多态
多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。
前提
有继承/实现关系;存在父类引用子类对象;存在方法重写。
注意:
多态是对象、行为的多态,Java中的属性(成员变量)没有多态。
好处
1.在多态形式下,右边对象是解耦合的,即右边对象可以随时切换。
2.定义方法时,使用父类类型的形参,可以接收一切子类对象。
注意:
多态下不能使用子类的独有功能。
类型转换
自动类型转换:父类 变量名 = new 子类(); People p1 = new Student();
强制类型转换:子类 变量名 = (子类) 父类变量; Student s1 = (Student) p1;
注意:
1.存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。
1 2
| People p1 = new Student(); Student s1 = (Student) p1;
|
2.运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException)的错误出来。
3.强制类型转换之前,使用instanceof关键字,判断当前对象的真实类型,再进行强转。
1 2 3 4 5 6 7 8
| public class People { public String name = "People";
public void run(){ System.out.println("人跑步"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Student extends People{ public String name = "Student";
@Override public void run(){ System.out.println("学生跑步"); }
public void test(){ System.out.println("学生考试"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Teacher extends People{ public String name = "Teacher";
@Override public void run(){ System.out.println("老师跑步"); }
public void teach(){ System.out.println("老师上课"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class Test { public static void main(String[] args) { People p1 = new Student(); p1.run();
System.out.println(p1.name);
Student s1 = (Student) p1; s1.run();
People p2 = new Teacher(); p2.run(); System.out.println(p2.name);
Student s = new Student(); go(s); Teacher t = new Teacher(); go(t); } public static void go(People p){ p.run(); if(p instanceof Student){ Student s = (Student) p; s.test(); }else if(p instanceof Teacher){ Teacher t = (Teacher) p; t.teach(); } } }
|
final
final关键字是最终的意思,可以修饰(类、方法、变量)
1.修饰类:该类被称为最终类,特点是不能被继承。
2.修饰方法:该方法被称为最终方法,特点是不能被重写。
3.修饰变量:该变量只能被赋值一次。
1 2 3 4 5 6 7
| final修饰变量: 一、局部变量:final int a; a = 12; 二、成员变量 1.静态成员变量:public static final double PI = "3.1415926";
2.实例成员变量:private final String name = "surourou";
|
注意:
1.final修饰基本类型的变量,变量存储的数据不能被改变。
2.final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容可以改变。
常量
static final修饰的成员变量就被称为常量。
执行原理
程序编译后,常量会被宏替换:出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能一样。
抽象类
abstract修饰类,这个类就是抽象类。
abstract修饰方法,这个方法就是抽象方法。抽象方法只有方法签名,不能有方法体。
1 2 3 4
| public abstract class A { public abstract void test(); }
|
注意:
1.抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
2.类该有的成员(成员变量、方法、构造器)抽象类都可以有。
3.抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
4.一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
模板方法设计模式
解决方法中存在重复代码的问题。
实现方法:
1 2 3 4
| 1、定义一个抽象类。 2、在里面定义2个方法: 模板方法:把相同代码放里面去。 抽象方法:具体实现交给子类完成。
|
注意:
使用final关键字修饰模板方法。模板方法是给对象直接使用的,不能被子类重写。一旦子类重写了模板方法,模板方法就失效。
接口
关键字interface定义接口。
1 2 3 4
| public interface A{ }
|
注意:
1.接口里面的成员变量是常量,成员方法是抽象方法,只有这两种东西。
2.接口内不能有构造器和代码块static{},所以接口不能创造对象。接口是用来被类实现implements的,实现接口的类称为实现类。
3.一个类可以实现多个接口(接口可以理解成干爹),实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。接口弥补了类单继承的不足。
1 2
| public class A implements A1, A2, A3{}
|
JDK8接口新增的三种方法
1.默认方法(实例方法):使用用default修饰,默认会被加上public修饰。只能使用接口的实现类对象调用。
2.私有方法:必须用private修饰(JDK 9开始才支持)。只能在接口内部调用。
3.类方法(静态方法):使用static修饰,默认会被加上public修饰。只能用接口名来调用。
1 2 3 4 5 6 7 8 9 10 11 12
| public interface A{ public default void test1(){} private void test2(){} public static void test3(){} }
|
接口是多继承的
1 2 3 4 5 6 7
| interface A{} interface B{} interface C{}
interface D extends A,B,C{}
class E implements D{}
|
注意:
1.一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
1 2 3 4 5 6 7 8
| interface I{ void test(); } interface J{ String test(); }
|
2.一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
1 2 3 4 5 6 7 8 9 10
| interface I{ void test(); } interface J{ String test(); }E
|
3.一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Fu{ public void run(){ System.out.println("父类的run方法"); } }
interface IT{ default void run(){ System.out.println("接口IT的run方法"); } }
class Zi extends Fu implements IT{}
|
4.一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| interface IT1{ default void test(){ System.out.println("IT1"); } }
interface IT2{ default void test(){ System.out.println("IT2"); } }
class N implements IT1, IT2{ @Override public void test(){ System.out.println("class N"); } }
|
内部类
成员内部类
1.成员内部类:类中的普通成员。成员内部类可以定义成员变量、构造器和成员方法。JDK16开始可以定义静态成员。
2.和实例方法一样,成员内部类的实例方法中,可以直接访问外部类的实例成员、静态成员。
3.成员内部类可以在成员内部类的实例方法中,拿到当前外部类对象,格式是:外部类名.this 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Outer { private int age = 99; public static String name;
public class Inner{ private String name; public static String gender; private int age = 88;
public void test(){ int age = 66; System.out.println(age); System.out.println(this.age); System.out.println(Outer.this.age); } } }
|
1 2 3
| Outer.Inner inner = new Outer().new Inner(); inner.test();
|
静态内部类
静态内部类:有static修饰的内部类,属于外部类自己持有。
注意:静态内部类可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员。(和静态方法类似)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Outer { private int age = 99; public static String name = "Outer"; public static class Inner{ public static String gender; private int a;
public void test(){ System.out.println(name); } } }
|
1 2 3
| Outer.Inner inner = new Outer.Inner(); inner.test();
|
局部内部类
局部内部类是定义在在方法中、代码块中、构造器等执行体中。
鸡肋语法。
匿名内部类
匿名内部类:一种特殊的局部内部类。匿名:指的是程序员不需要为这个类声明名字。
特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象。
作用:
1.用于更方便的创建一个子类对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Test { public static void main(String[] args) { Animal animal = new Animal() { @Override public void cry() { System.out.println("猫在哭"); } }; animal.cry(); } }
abstract class Animal{ public abstract void cry(); }
|
1 2 3 4 5 6 7 8 9
| class Test$1 extends Animal { Test$1() { }
public void cry() { System.out.println("猫在哭"); } }
|
2.作为一个参数传输给方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Test { public static void main(String[] args) { go(new Swimming() { @Override public void swim() { System.out.println("狗在游泳"); } }); }
public static void go(Swimming s){ s.swim(); } }
interface Swimming{ void swim(); }
|
枚举
枚举是一种特殊类。
枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开。这些名称本质是常量,每个常量都会记住枚举类的一个对象。
1 2 3 4 5
| public enum A { X,Y,Z; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| A a1 = A.X; System.out.println(a1);
A[] as = A.values(); for (int i = 0; i < as.length; i++) { System.out.println(as[i]); }
A a2 = A.valueOf("Y"); System.out.println(a2.name()); System.out.println(a2.ordinal());
|
下面是枚举类反编译的代码:

抽象枚举
在枚举类中定义抽象方法就是抽象枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public enum B { X { @Override public void go() {
} },Y("surourou") { @Override public void go() { System.out.println(this.getName()+"你好"); } }; public abstract void go();
private String name; private B(){} private B(String name){this.name=name;}
public String getName() { return name; }
public void setName(String name) { this.name = name; } }
|
枚举类实现单例
应用
枚举类用来表示一组信息,然后作为参数进行传输。
1 2 3
| public enum Constant { BOY, GIRL; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(String[] args) { check(Constant.BOY); check(Constant.GIRL); } public static void check(Constant sex){ switch (sex){ case Constant.BOY: System.out.println("男孩"); break; case Constant.GIRL: System.out.println("女孩"); break; } }
|
泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>) ,称为泛型类、泛型接口,泛型方法,统称为泛型。如:ArrayList是泛型类。
本质:把具体的数据类型作为参数传给类型变量。
泛型类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class MyArrayList <E>{ private Object[] arr = new Object[10]; private int size = 0;
public boolean add (E e){ arr[size++] = e; return true; }
public E get(int index){ return (E) arr[index]; } }
|
1 2 3 4 5 6 7
| MyArrayList<String> list = new MyArrayList<>(); list.add("srr"); list.add("girl"); list.add("18"); String s = list.get(0); System.out.println(s);
|
其他类型的泛型类定义:
1 2
| class MyClass1<E, T>{} class MyClass2<E extends Animal>{}
|
泛型接口
1 2 3 4 5 6
| public interface Data<T> {
void add(T t); ArrayList<T> getByName(String name); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class StudentData implements Data<Student>{ @Override public void add(Student student) {
}
@Override public ArrayList<Student> getByName(String name) { return null; } }
|
泛型方法
1 2 3 4 5 6 7 8 9 10 11 12
| public static void main(String[] args) { String s1 = test("surourou"); System.out.println(s1);
Student st = test(new Student()); System.out.println(st); }
public static <T> T test(T t){
return t; }
|
通配符?
通配符?:在使用泛型时可以代表一切类型
? extends Car代表上限,?只能是Car或者Car的子类。
? super Car代表下限,?只能是Car或者Car的父类。
1 2
| public static void test2(ArrayList<? extends Student> t){} public static void test2(ArrayList<?> t){}
|
泛型擦除
泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
1 2 3 4 5
| ArrayList<String> a = new ArrayList<>(); a.add("srr"); a.add("girl"); String str = a.get(0);
|
1 2 3 4 5 6
| ArrayList a = new ArrayList(); a.add("srr"); a.add("girl"); String str = (String)a.get(0);
|
注意:泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
1 2 3 4 5 6
|
ArrayList<Integer> a3 = new ArrayList<>(); a3.add(1); ArrayList<Double> a4 = new ArrayList<>(); a4.add(1.0);
|