面向对象高级

static关键字

static用来修饰的结构:属性、方法;代码块、内部类

我们可以按照变量在类中声明的位置,分为局部变量和成员变量。对于成员变量又可以进一步分类:
对于加了static关键字的变量,我们称为静态变量、类变量;
对于没有加static关键字的变量,我们称为实例变量、非静态变量。

静态变量和实例变量对比

静态变量 实例变量
个数 只有一份,被类的多个对象共享 每一个对象都保存有一份
内存位置 jdk6及之前,在方法区,jdk7及之后再堆 存放在堆空间的对象实体中
生命周期 随类的加载而加载,随类的卸载而消亡 随对象的创建而加载,随对象的消亡儿消亡
调用者 可被类直接调用,也可被对象调用 只能被对象调用

static修饰方法

  • 随着类的加载而加载

  • 可以通过“类.静态方法”的方式,直接调用静态方法

  • 静态方法内可以调用静态的属性或静态的方法,不可以调用非静态的结构(比如属性和方法)

  • 静态方法可被类直接调用,也可被对象调用;实例方法只能被对象调用

  • 静态方法内不能用this和super(理解:因为静态方法随类的加载而加载,而this和super都是需要产生对象才可以使用的,但是VM是先加载类再加载对象,所以静态方法使用this和super显然是不合适的)

什么时候要使用static?

  • 属性:1)当前类的对象是否能共享此属性,且此变量值相同。2)常量
  • 方法:方法内操作的变量都是静态变量。工具类的方法,如:Arrays、Maths

单例(Singleton)设计模式

饿汉式

1
2
3
4
5
6
7
8
9
10
11
class Bank{
private Bank(){//1.私有化构造器
}
//2.创建实例
//4.声明为static
private static Bank instace = new Bank();
//3.获取实例
public static Bank getInstance(){
return instance;
}
}

“立即加载”,随着类的加载就创建了
优点:写法简单,运行较快,是线程安全的
缺点:内存占用时间长

懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class GirlFriend{
private Bank(){//1.私有化构造器
}
//2.声明实例
//4.声明为static
private static Bank instace =null;
//3.获取实例,如果没有创建对象,则在方法内再创建
public static Bank getInstance(){
if(instance == null){
instance=new Bank();
}
return instance;
}
}

“延迟加载”,在需要的时候才创建对象
优点:节省内存
缺点:线程不安全

类的成员之四:代码块

代码块,也叫初始化块。是用来初始化类或对象的信息。按照是否加static修饰分为静态代码块和非静态代码块。静态代码块随着类的加载而执行,由于类的加载只会执行一次,所以静态代码块只会执行一次;非静态代码块随着对象的创建而执行,没创建一个实例,都会执行一次。静态代码块要先于非静态代码块执行。

作用:可以把不同构造器中相同的部分写在代码块中进行赋值;对静态变量进行赋值。

例子:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
public class UserTest2 {
public static void main(String[] args) {
User2 user1 = new User2();
System.out.println(user1.getInfo());
System.out.println("====================================");
User2 user2 = new User2("KuoZ", "80841007");
System.out.println(user2.getInfo());
}
}
class User2{
private String userName;
private String password;
private long registrationTime;//注册时间
public User2() {

this.userName=""+System.currentTimeMillis();
this.password="123456";
}

public User2(String userName, String password) {
this.userName = userName;
this.password = password;
}
{//使用非静态代码块
System.out.println("新用户注册");
this.registrationTime = System.currentTimeMillis();
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public long getRegistrationTime() {
return registrationTime;
}

public String getInfo() {
return "用户名:" + userName +
", 密码:" + password +
", 注册时间:" + registrationTime;
}
}

总结:属性赋值的位置及过程

final关键字

final可以修饰类、方法、变量

final修饰类时,则此类无法被继承
final修饰方法时,则此方法无法被重写
final修饰变量时,则表示常量,无法被更改

你就是我的final了!

abstract关键字

abstract关键字可以修饰类和方法

abstract修饰类:

  • 此类称为抽象类
  • 不能实例化
  • 抽象类要包含构造器,因为子类对象实例化时会间接调用父类的构造器
  • 抽象类中不一定要有抽象方法,而抽象方法所在的类必须是抽象类

abstract修饰方法:

  • 此方法即为抽象方法
  • 抽象方法只有方法的声明,没有方法体
  • 抽象方法的功能是确定的,只是不知道其如何实现的
  • 子类必须重写父类所有抽象方法之后才可实例化,否则必须声明为抽象类

abstract不能修饰的结构:属性、构造器、代码块

与abstact矛盾的关键字:
私有方法:因为私有方法不能被重写
静态方法:阻碍静态方法使用类调用
final方法:不能重写
final类:不能继承

接口(interface)

接口的使用:

接口的本质是一个契约、规范、标准,制定好后大家都要遵守。

接口内部结构:

可以声明:
属性:必须使用public static final修饰
方法:jdk8之前:声明抽象方法,修饰为public abstract
jdk8:声明了静态方法、默认方法
jdk9:声明私有方法

不可以声明构造器、代码块等

1
2
3
4
5
6
    //全局常量 默认即为 public static final,可以省略
public static final int MIN_SPEED = 0;
public static final int MAX_SPEED = 7900;
//方法 默认即为 public abstract,可以省略
public abstract void fly();
}

接口和类的关系:实现关系

格式class A extends SuperA implements B,C{}
A是SuperA的子类,是B、C的实现类

类可以实现多个接口,这种多实现性一定程度上弥补了单继承的局限性。类必须将实现的接口中的所有抽象方法都重写才能实例化,否则必须声明为抽象类。

接口和接口之间是可以有继承关系的,而且是多继承

接口的多态性

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class InterfaceTest {
public static void main(String[] args) {
//1.创建接口实现类的对象(camera)
Camera camera = new Camera();
Computer computer = new Computer();
computer.DataTransfer(camera);

//2.创建接口实现类的匿名对象
computer.DataTransfer(new printer());

//3.创建接口匿名实现类的对象
USB usb1 =new USB(){
@Override
public void start() {
System.out.println("usb1 start");
}
@Override
public void stop() {
System.out.println("usb1 stop");
}
};
computer.DataTransfer(usb1);
//4.创建接口匿名实现类的匿名对象
computer.DataTransfer(new USB() {
@Override
public void start() {
System.out.println("usb2 start");
}

@Override
public void stop() {
System.out.println("usb2 stop");
}
});
}
}
interface USB{//接口定义
//属性
//方法
void start();
void stop();
}
class Computer implements USB{//实现类
@Override
public void start() {
System.out.println("computer start");
}

@Override
public void stop() {
System.out.println("computer stop");
}
public void DataTransfer(USB usb){
usb.start();
System.out.println("Data Transfer");
usb.stop();
}
}
class Camera implements USB{//实现类

@Override
public void start() {
System.out.println("camera start");
}

@Override
public void stop() {
System.out.println("camera stop");
}
}
class printer implements USB{//实现类

@Override
public void start() {
System.out.println("printer start");
}
@Override
public void stop() {
System.out.println("printer stop");
}
}

区分抽象类和接口:相同:都可以声明抽象方法;都不能实例化。不同:抽象类一定要有构造器,接口没有;关系不同。(指继承、实现、多继承)

jdk8和jdk9中接口的新特性

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class ComparaATest {
public static void main(String[] args) {
ComparaA.method1();
//SubClass.method1();1.静态方法不能被实现类调用
SubClass subClass = new SubClass();
subClass.method2();//2.默认方法可以调用,并且可以重写
//3.接口冲突:类实现了两个接口,两个接口都有同名的默认方法,会报错
//解决方法:在实现类中重写该方法
subClass.method3();
//4.类优先原则
subClass.method4();//优先调用了父类的method4
System.out.println("-----------------");
//5.调用接口、父类的方法
subClass.method();
}
}
interface ComparaA {
//属性不变
//方法可声明静态方法
public static void method1() {
System.out.println("ComparaA method1");
}

//方法可声明默认方法
default void method2() {
System.out.println("ComparaA method2");
}
default void method3() {
System.out.println("ComparaA method3");
}
default void method4() {
System.out.println("ComparaA method4");
}
}
interface ComparaB {
default void method3() {
System.out.println("ComparaB method3");
}
}
class SubClass extends SuperClass implements ComparaA, ComparaB {
//重写接口中的默认方法
@Override
public void method3() {
System.out.println("SubClass method3");
}
public void method(){
method3();
super.method4();
ComparaA.super.method3();
ComparaB.super.method3();
}
}
class SuperClass{
public void method4() {
System.out.println("SuperClass method4");
}
}

类的成员之五:内部类

内部类的分类:

成员内部类:声明在外部类的里面
又分为是否使用static修饰,静态的成员内部类,反之为非静态。

局部内部类:声明在构造器、方法、代码块内
又分为是否匿名

成员内部类的理解

  • 从类的角度看
    • 可以声明属性、方法、构造器、代码块、内部类等
    • 可以声明父类、实现接口
    • 可以使用final修饰
    • 可使用abstract修饰
  • 从外部类的成员的角度看
    • 在内部可调用外部类的的结构
    • 可以声明为private(与外部类不同,外部类只能用public、缺省)
    • 可以使用static修饰

如何创建成员内部类的实例

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
public class InClassTest {
public static void main(String[] args) {
//1.静态成员内部类实例化
Person.Dog dog = new Person.Dog();
dog.eat();
//2.非静态成员内部类实例化
Person person = new Person();
Person.Bird bird = person.new Bird();
bird.eat();
}

}
class Person{
String name="张三";
//静态成员内部类
static class Dog{
public void eat(){
System.out.println("Dog eat");
}
}
//非静态成员内部类
class Bird {
String name="鸮";
public void eat() {
System.out.println("Bird eat");
}
}
}

如何在成员内部类中调用外部类的结构

1
2
3
4
5
public void show(String name){
System.out.println(name);//形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}

局部内部类的使用

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
public class InnerTest2 {

//开发中的场景
public Comparable getInstace() {
//1.返回实现类的有名对象
class MyComparable implements Comparable {
@Override
public int compareTo(Object o) {
return 0;
}
}
MyComparable myComparable = new MyComparable();
return myComparable;
//2.返回实现类的匿名对象
// class MyComparable implements Comparable {
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// }
//
// return new MyComparable();

//3.返回匿名实现类的有名对象
// Comparable c = new Comparable() {
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// };
// return c;
//4.返回匿名实现类的匿名对象
// return new Comparable() {
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// };
}
}

例子:

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
public class InnerTest3 {
public static void main(String[] args) {
new A(){//接口匿名实现类的匿名对象
@Override
public void Method() {
System.out.println("hello");
}
}.Method();
new B(){//抽象类匿名子类的匿名对象
@Override
void Method() {
System.out.println("hello2");
}
}.Method();
new C(){//匿名子类的匿名对象
void Method(){
System.out.println("hello3");
}
}.Method();
}
}
interface A{//接口
void Method();
}
abstract class B{//抽象类
abstract void Method();
}
class C{//普通的一个父类
void Method(){}
}

综上,针对抽象类(父类)、接口,都可通过上述方式创建**匿名子类(实现类)**的匿名对象来实现方法。

枚举类

枚举类本质上也是类,不过这个类的对象是有限、确定的,用户不能随意创建。

枚举类声明格式
enum 枚举名{},在枚举类中,必须先声明多个对象,对象之间用“,”连接。枚举类的属性必须为private final

参考代码及常用方法测试

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
42
public class EnumTest {
public static void main(String[] args) {
//1.toString()方法
System.out.println(Season.SPRING);
//2.name()方法
System.out.println(Season.SPRING.name());
//3.values()方法
Season[] seasons = Season.values();
for (int i = 0; i < seasons.length; i++) {
System.out.println(seasons[i]);
}
//4.valueOf(String objName)方法 返回名称为objName的对象,如果不存在则报错
String objName = "SUMMER";
Season season = Season.valueOf(objName);
System.out.println(season);
String wrongObjName = "SUMMER1";
//Season season1 = Season.valueOf(wrongObjName);
//报错:java.lang.IllegalArgumentException
//5.ordinal()方法
System.out.println(Season.SPRING.ordinal()); //0
}
}
enum Season{
//对象
SPRING("春天", "温暖"),
SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"),
WINTER("冬天", "寒冷");
private final String seasonName;
private final String seasonDesc;
private Season(String seasonName,String seasonDesc){
this.seasonName=seasonName;
this.seasonDesc=seasonDesc;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}

枚举实现接口的操作:

  1. 枚举类实现接口,在枚举类中重写接口的抽象方法。当通过不同的枚举类对象调用此方法时,执行同一个方法
  2. 枚举类每一个对象重写接口的抽象方法。当通过不同的枚举类对象调用此方法时,执行不同方法

针对2,要让枚举类的每一个对象重写接口的抽象方法,具体操作参考如下:

1
2
3
4
5
6
SPRING("春天", "温暖"){
@Override
public void show() {
System.out.println("春天来了");
}
},

注解

注解(annotation),与注释类似但并不相同,注解是可以被编译器读取的。

常见的Java内置注解:

@override用于检查重写方法是否写对。

@Deprecated表示某个类或方法已经不推荐使用,调用时会有警告提示。调用被它标记的类或方法时会出现删除线。

@SuppressWarnings告诉编译器忽略特定类型的警告

框架 = 注解 + 反射 + 设计模式

元注解:注解的注解

包装类

概述

使用包装类的原因:为了使基本数据类型具备引用数据类型变量的特征,给8中基本数据类型都提供了相应的包装类。

基本数据类型的包装类
对于8种数据类型,int、char的包装类为Integer、Character,byte、short、long、float、double、boolean为将首字母大写。

基本数据类型与引用数据类型的转换:
(装箱)基本数据类型–>包装类:1.使用包装类的构造器 2.(建议)使用包装类的valueOf()
(拆箱)包装类–>基本数据类型:调用包装类的xxxValue()

jdk5.0新特性:自动装箱和自动拆箱,直接赋值就能自动装/拆

String和基本数据类型、包装类的转换

基本数据类型、包装类–>String类型:1.调用String类重载的方法valueOf();2.使用基本数据类型/包装类+“”

String类型–>基本数据类型、包装类:调用包装类的静态方法:parseXxx()

IDEA快捷键

功能 快捷键
使用xx块环绕-surround with ctrl+alt+t
复制指定行代码 ctrl+d
删除指定行代码 ctrl+y
切换到上一行代码空位 ctrl+alt+enter
上下移动代码 ctrl+shift+↑/↓
上下移动代码 alt+shift+↑/↓
形参列表提醒 ctrl+p
批量修改 shift+f6
重写父类的方法 ctrl+o
实现接口的方法 ctrl+i
查看源码 ctrl+click或者ctrl+n
打开的类文件之间切换 ctrl+alt+←/→
切换标签 alt+←/→
查看继承树关系 ctrl+h
类的UML关系图 ctrl+alt+u
全项目搜索文本 ctrl+shift+f
格式化代码 ctrl+alt+l

哎,咱快累死了,不过终于是把这面向对象学完了。

说起来明天咱居然就要20岁了,感觉好突然呐Σ( ° △ °|||)︴

附一张赫萝表情包

Holo