关键字static

​ 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。

如果想让一个类的所有实例共享数据,就用类变量!

类属性、类方法的设计思想

  • 类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
  • 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。

static

使用范围

​ 在Java类中,可用static修饰属性、方法、代码块、内部类

被修饰后的成员的特点

  • 随着类的加载而加载
  • 优先于对象存在
  • 修饰的成员,被所有对象所共享
  • 访问权限允许时,可不创建对象,直接被类调用

类变量(class Variable)

  • 类变量(类属性)由该类的所有实例共享

类方法(class Method)

  • 没有对象的实例时,可以用类名.方法名()的形式访问由static标记的类方法。
  • 在static方法内部只能访问类的static属性,不能访问类的非static属性。
  • 因为不需要实例就可以访问static方法,因此static方法内部不能有this。(也不能有super ? YES!)
  • 重载的方法需要同时为static的或者非static的。

static应用

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
55
package L05.exer1;

//static的一个应用:使用静态的变量可以实现"累加"的效果,因为静态的变量在内存中独一份!
public class TestCircle {
public static void main(String[] args) {
Circle circle1 = new Circle(2.0);
Circle circle2 = new Circle(4.0);
System.out.println(Circle.getTotal());
Circle.show();
circle1.setInfo("我是一个帅气的圆");
Circle.show();
}
}

class Circle{
private double radius; //半径
private static String info = "我是一个圆";
private static int total = 0;

public Circle(double radius){
this.radius = radius;
total ++;
}

public double getRadius() {
return radius;
}

public void setRadius(double radius) {
this.radius = radius;
}

public static String getInfo() {
return info;
}

public void setInfo(String info) {
Circle.info = info;
}

public static int getTotal() {
return total;
}

public static void show(){
System.out.println(info);
}

@Override
public String toString() {
return "Circle{" +
"radius=" + radius +
'}';
}
}

练习

​ 编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。编写主类,使用银行账户类,输入、输出3个储户的上述信息。考虑:哪些属性可以设计成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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package L05.exer1;
/*
编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。
编写主类,使用银行账户类,输入、输出3个储户的上述信息。
考虑:哪些属性可以设计成static属性。
*/
public class TestAccount {
public static void main(String[] args) {
Account account1 = new Account("abc123",1000);
Account account2 = new Account("abc456",2000);
System.out.println(account1);
System.out.println(account2);
}
}
class Account{
private int id; //账号
private String password; //密码
private double balance; //存款余额
private static double rate = 0.05; //利率
private static double minBalance = 1; //最小余额
private static int init = 1000;

public Account(String password,double balance){
this.id = init++;
this.password = password;
this.balance = balance;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getPassword() {
return password;
}

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

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}

public static double getRate() {
return rate;
}

public static void setRate(double rate) {
Account.rate = rate;
}

public static double getMinBalance() {
return minBalance;
}

public static void setMinBalance(double minBalance) {
Account.minBalance = minBalance;
}

@Override
public String toString() {
return "Account{" +
"id=" + id +
", password='" + password + '\'' +
", balance=" + balance +
'}';
}
}

单例 (Singleton)设计模式

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。

​ 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的

解决的问题:使得一个类只能够创建一个对象。

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package L05.exer1;
//设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
//饿汉式
public class TestSingleton {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
class Singleton{
// 1、私有化构造器,使得在类的外部不能够调用此构造器
private Singleton(){
}
// 2、在类的内部创建一个类的实例
private static Singleton instance = new Singleton();

// 3、私有化此对象,通过公共的方法来调用
// 4、此公共的方法只能通过类来调用,所以设置为static的,同时类的实例也必须为static声明
public static Singleton getInstance(){
return instance;
}
}

懒汉式

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
package L05.exer1;

//懒汉式
public class TestSingleton2 {
public static void main(String[] args) {
Singleton2 singleton1 = Singleton2.getInstance();
Singleton2 singleton2 = Singleton2.getInstance();
System.out.println(singleton1 == singleton2);
}
}

class Singleton2{
// 1、私有化构造器,使得在类的外部不能够调用此构造器
private Singleton2(){
}
// 2、在类的内部创建一个类的实例
private static Singleton2 instance = null;

// 3、私有化此对象,通过公共的方法来调用
// 4、此公共的方法只能通过类来调用,所以设置为static的,同时类的实例也必须为static声明
public static Singleton2 getInstance(){
if (instance == null){
instance = new Singleton2();
}
return instance;
}
}
  • 暂时懒汉式还存在线程安全问题,讲到多线程时,可修复

举例

  • java.lang.Runtime

理解main方法的语法

​ 由于java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。

main()方法的使用

参数的使用

1
2
3
4
5
6
7
8
9
package L05.exer2;

public class TestMain {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++){
System.out.println("arg[" + i + "]=" + args[i]);
}
}
}

当作普通方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package L05.exer2;

public class TestMain2 {
public static void main(String[] args) {
Main.main(new String[10]);
}
}

class Main {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++){
args[i] = "value_" + i;
System.out.println(args[i]);
}
}
}

类的成员之四:初始化块

作用

​ 初始化块(代码块)作用:

  • 对Java对象进行初始化

执行顺序

  • 声明成员变量的默认值
  • —>显式初始化、多个初始化块依次被执行(同级别下按先后顺序执行)
  • —>构造器再对成员进行赋值操作

非静态代码块

  • 没有static修饰的代码块
    • 可以有输出语句。
    • 可以对类的属性、类的声明进行初始化操作。
    • 可以调用静态的变量或方法。
    • 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
    • 每次创建对象的时候,都会执行一次。且先于构造器执行

静态代码块

​ 一个类中初始化块若有修饰符,则只能被static修饰,称为静态代码块(static block ),当类被载入时,类属性的声明和静态代码块先后顺序被执行,且只被执行一次

​ static块通常用于初始化static (类)属性

  • 用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
package L05.exer3;

class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:" + msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("小新");
System.out.println("ִ执行Leaf的构造器");
}
}
public class TestLeaf{
public static void main(String[] args){
new Leaf();
//new Leaf();
}
}

关键字final

​ 在Java中声明类、属性和方法时,可使用关键字final来修饰,表示“最终”。

  • final标记的类不能被继承。提高安全性,提高程序的可读性。

    • String类、System类、StringBuffer类
  • final标记的方法不能被子类重写。

    • Object类中的getClass()
  • final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。

    • final标记的成员变量必须在声明的同时或在每个构造方法中或代码块中显式赋值,然后才能使用。
    • final double PI=3.14

final修饰类

1
2
3
4
final class A{
}
class B extends A{ //错误,不能被继承。
}

final修饰方法

1
2
3
4
5
6
7
8
9
10
class A{
public final void print(){
System.out.println(“A”);
}
}
class B extends A{
public void print(){ //错误,不能被重写。
System.out.println(“小新”);
}
}

final修饰变量—常量

1
2
3
4
5
class  A{
private final String INFO = “小新”; //声明常量
public void print(){
}
}
  • 常量名要大写,内容不可修改。
  • 不能使用默认初始化
  • static final:全局常量

抽象类(abstract class)

​ 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类

抽象类和抽象方法

  • 用abstract关键字来修饰一个类时,这个类叫做抽象类

    • 抽象类有构造器(凡是类都有构造器)
  • 用abstract来修饰一个方法时,该方法叫做抽象方法

    • 抽象方法:只有方法的声明,没有方法的实现。以分号结束:abstract int abstractMethod( int a );
  • 含有抽象方法的类必须被声明为抽象类

  • 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。

  • 不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。

练习

练习:编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。提供必要的构造器和抽象方法:work()。对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。

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
package L05.exer4;
/*
编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。
提供必要的构造器和抽象方法:work()。
对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。
请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。
*/
public class TestEmployee {
public static void main(String[] args) {
Employee employee = new Manager();
employee.work();

Employee employee1 = new CommonEmployee();
employee1.work();
}
}

//普通员工
class CommonEmployee extends Employee{
public void work(){
System.out.println("在流水线工作!");
}
}

//管理者
class Manager extends Employee{
private double bonus;

public void work(){
System.out.println("监督员工的工作,提高工作效率!");
}

public double getBonus() {
return bonus;
}

public void setBonus(double bonus) {
this.bonus = bonus;
}
}

//员工抽象类
abstract class Employee{
private String name;
private int id;
private double salary;

public abstract void work();

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}
}

模板方法设计模式(TemplateMethod)

​ 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

  • 解决的问题:
    • 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
    • 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
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
package L05.exer4;

//模版方式设计模式
public class TestTemplate {
public static void main(String[] args) {
new SubTemplate().spendTime();
}
}

abstract class Template{

public abstract void code();

public void spendTime(){
long start = System.currentTimeMillis();
this.code();
long end = System.currentTimeMillis();
System.out.println("花费的时间:" + (end - start));
}
}

class SubTemplate extends Template{
public void code(){
boolean flag = false;
for (int i = 2; i <= 100000; i++){
for (int j = 2; j <= Math.sqrt(i); j++){
if (i % j == 0){
flag = true;
break;
}
}
if (!flag){
System.out.println(i);
}
flag = false;
}
}
}

更彻底的抽象—接口(interface)

概念

  • 有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
  • 接口(interface)是抽象方法常量值的定义的集合。
  • 从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
  • 实现接口类:class SubClass implements InterfaceA{ }
  • 一个类可以实现多个接口,接口也可以继承其它接口。

特点

  • interface来定义
  • 接口中的所有成员变量都默认是由public static final修饰的
  • 接口中的所有方法都默认是由public abstract修饰的
  • 接口没有构造器
  • 接口采用多继承机制
  • 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
  • 接口的主要用途就是被实现类实现。(面向接口编程)
  • 与继承关系类似,接口与实现类之间存在多态性
  • 定义Java类的语法格式:先写extends,后写implements

接口的多态性

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
package L05.exer5;

//接口与具体的实现类之间也存在多态性
public class TestInterface {

public static void main(String[] args) {
Duck duck = new Duck();
TestInterface.test1(duck);
TestInterface.test2(duck);
TestInterface.test3(duck);
}
public static void test1(Runner runner){
runner.run();
}
public static void test2(Swimmer swimmer){
swimmer.swim();
}
public static void test3(Flier flier){
flier.fly();
}
}

interface Runner{
public abstract void run();
}

interface Swimmer{
void swim();
}

interface Flier{
void fly();
}

class Duck implements Runner,Swimmer,Flier{

@Override
public void run() {
System.out.println("鸭子也能牛牛的走路~");
}

@Override
public void swim() {
System.out.println("红掌拨清波");
}

@Override
public void fly() {
System.out.println("丑小鸭也可以变成白天鹅!");
}
}

用法总结

  • 通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。

  • 通过接口可以指明多个类需要实现的方法,一般用于定义对象的扩张功能。

  • 接口主要用来定义规范。解除耦合关系。

工厂方法(FactoryMethod)

概述

​ 定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。

适用性

  • 当一个类不知道它所必须创建的对象的类的时候
  • 当一个类希望由它的子类来指定它所创建的对象的时候
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候

示例

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
package L05.exer5;

//工厂方法的设计模式
public class TestFactoryMethod {
public static void main(String[] args) {
IWorkFactory iWorkFactory = new StudentWorkFactory();
iWorkFactory.getWork().doWork();
IWorkFactory iWorkFactory1 = new TeacherWorkFactory();
iWorkFactory1.getWork().doWork();
}
}

interface IWorkFactory{
Work getWork();
}

class StudentWorkFactory implements IWorkFactory{

@Override
public Work getWork() {
return new StudentWork();
}
}

class TeacherWorkFactory implements IWorkFactory{

@Override
public Work getWork() {
return new TeacherWork();
}
}

interface Work{
void doWork();
}

class StudentWork implements Work{

@Override
public void doWork() {
System.out.println("学生写作业~");
}
}

class TeacherWork implements Work{

@Override
public void doWork() {
System.out.println("老师批改作业~");
}
}

总结

​ FactoryMethod模式是设计模式中应用最为广泛的模式,在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要。FactoryMethod解决的就是这个问题,它通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。

代理模式(Proxy)

概述

​ 为其他对象提供一种代理以控制对这个对象的访问。

示例

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
package L05.exer5;

//接口的应用:代理模式
public class TestProxy {
public static void main(String[] args) {
Object object = new ProxyObject();
object.action();
}
}

interface Object{
void action();
}

//代理类
class ProxyObject implements Object{
Object object;

public ProxyObject() {
System.out.println("代理类创建成功");
object = new ObjectImpl();
}

public void action(){
System.out.println("代理类开始执行");
object.action();
System.out.println("代理类执行结束");
}
}

//被代理类
class ObjectImpl implements Object{

@Override
public void action() {
System.out.println("=====被代理类开始执行======");
System.out.println("=====具体的操作======");
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package L05.exer5;

public class TestUSB {
public static void main(String[] args) {
Computer computer = new Computer();
computer.doWork(new Printer());
Flash flash = new Flash();
computer.doWork(flash);

//实现接口的匿名类对象
USB phone = new USB() {
@Override
public void start() {
System.out.println("手机开始连接");
}

@Override
public void stop() {
System.out.println("手机停止连接");
}
};
computer.doWork(phone);
}
}

class Computer{
public void doWork(USB usb){
usb.start();
System.out.println("=====此设备开始操作=====");
usb.stop();
}
}

interface USB{
void start();
void stop();
}

//打印机
class Printer implements USB{

@Override
public void start() {
System.out.println("打印机开始工作");
}

@Override
public void stop() {
System.out.println("打印机结束工作");
}
}

//U盘
class Flash implements USB{

@Override
public void start() {
System.out.println("U盘开始工作");
}

@Override
public void stop() {
System.out.println("U盘结束工作");
}
}

接口练习

  • 定义一个接口用来实现两个对象的比较。

    1
    2
    3
    interface CompareObject{
    public int compareTo(Object o); //若返回值是0, 代表相等;若为正数,代表当前对象大;负数代表当前对象小
    }
  • 定义一个Circle类

  • 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。

  • 定义一个测试类TestCircle,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。

  • 思考:参照上述做法定义矩形类Rectangle和ComparableRectangle类,在ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小。

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
package L05.exer5;

public class TestCircle {
public static void main(String[] args) {
ComparableCircle c1 = new ComparableCircle(2.1);
ComparableCircle c2 = new ComparableCircle(2.2);
ComparableCircle c3 = new ComparableCircle(2.1);
System.out.println(c1.compareTo(c2)); // -1
System.out.println(c1.compareTo(c3)); // 0
System.out.println(c2.compareTo(c3)); // 1
}
}

interface CompareObject{
int compareTo(java.lang.Object object); //若返回值是0,代表相等; 若为正数,代表当前对象大;负数代表当前对象小
}

class Circle{
private double radius;

public Circle() {
super();
}

public Circle(double radius) {
super();
this.radius = radius;
}

public double getRadius() {
return radius;
}

public void setRadius(double radius) {
this.radius = radius;
}
}

class ComparableCircle extends Circle implements CompareObject{

public ComparableCircle(double radius){
super(radius);
}

@Override
public int compareTo(java.lang.Object object) {
if (this == object){
return 0;
}
else if (object instanceof ComparableCircle){
ComparableCircle c = (ComparableCircle) object;
if (this.getRadius() > c.getRadius()){
return 1;
}
else if (this.getRadius() < c.getRadius()){
return -1;
}
else
return 0;
}
else
throw new RuntimeException("传入的非ComparableCircle的对象,不可进行比较");
}
}

抽象类练习

题目

​ 编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。

目的

​ 对象、继承、封装、多态、抽象类的组合应用

说明

  • 定义一个Employee类,该类包含:

    • private成员变量name,number,birthday,其中birthday为MyDate类的对象;
    • abstract方法earnings();toString()方法输出对象的name,number和birthday
  • MyDate类包含:

    • private成员变量month,day,year;
    • toDateString()方法返回日期对应的字符串:xxxx年xx月xx日
  • 定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:

    • private成员变量monthlySalary;
    • 实现父类的抽象方法earnings(),该方法返回monthlySalary值;
    • toString()方法输出员工类型信息及员工的name,number,birthday
  • 参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:

    • private成员变量wage和hour;
    • 实现父类的抽象方法earnings(),该方法返回wage*hour值;
    • toString()方法输出员工类型信息及员工的name,number,birthday
  • 定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。

代码

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
package L05.exer6;

public abstract class Employee {
private String name;
private int number;
private MyDate birthday;

public abstract double earnings();

public Employee(String name, int number, MyDate birthday) {
super();
this.name = name;
this.number = number;
this.birthday = birthday;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

public MyDate getBirthday() {
return birthday;
}

public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}

@Override
public String toString() {
return "name='" + name + '\'' +
", number=" + number +
", birthday=" + birthday.toDateString();
}
}
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
package L05.exer6;

public class MyDate {
private int month;
private int day;
private int year;

public MyDate(int year, int month, int day) {
super();
this.month = month;
this.day = day;
this.year = year;
}

public int getMonth() {
return month;
}

public void setMonth(int month) {
this.month = month;
}

public int getDay() {
return day;
}

public void setDay(int day) {
this.day = day;
}

public int getYear() {
return year;
}

public void setYear(int year) {
this.year = year;
}

public String toDateString() {
return year + "年" + month + "月" + day + "日";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package L05.exer6;

public class SalariedEmployee extends Employee{
private double monthlySalary; //月工资

public SalariedEmployee(String name, int number, MyDate birthday, double monthlySalary) {
super(name, number, birthday);
this.monthlySalary = monthlySalary;
}

@Override
public double earnings() {
return monthlySalary;
}

@Override
public String toString() {
return "SalariedEmployee{" + super.toString() +
"}, monthlySalary=" + monthlySalary;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package L05.exer6;

public class HourlyEmployee extends Employee{
private double wage;
private double hour;

public HourlyEmployee(String name, int number, MyDate birthday, double wage, double hour) {
super(name, number, birthday);
this.wage = wage;
this.hour = hour;
}

@Override
public double earnings() {
return wage * hour;
}

@Override
public String toString() {
return "HourlyEmployee{" + super.toString() +
"}, wage=" + wage +
", hour=" + hour;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package L05.exer6;

import java.util.Scanner;

public class PayrollSystem {
public static void main(String[] args) {
Employee[] employees = new Employee[2];
employees[0] = new SalariedEmployee("小新",1001,new MyDate(1998,10,29),8800);
employees[1] = new HourlyEmployee("小伟",1002,new MyDate(1998,9,24),100,6);
Scanner scanner = new Scanner(System.in);
System.out.println("请输入本月的月份:");
int month = scanner.nextInt();
for (int i = 0 ; i < employees.length; i++){
if (month == employees[i].getBirthday().getMonth()){
System.out.println("加工资:加100~");
}
System.out.println(employees[i]);
}
}

类的成员之五:内部类

概述

  • 在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。

  • Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。

    • Inner class的名字不能与包含它的类名相同
  • Inner class可以使用外部类的私有数据,因为它是外部类的成员,同一个类的成员之间可相互访问。而外部类要访问内部类中的成员需要:内部类.成员或者内部类对象.成员

分类

  • 成员内部类(static成员内部类和非static成员内部类)
  • 局部内部类(不谈修饰符)
  • 匿名内部类

特性

  • Inner class作为类的成员:

    • 可以声明为final
    • 和外部类不同,Inner class可声明为privateprotected
    • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量
  • Inner class作为类:

    • 可以声明为abstract类 ,因此可以被其它的内部类继承

【注意】非static的内部类中的成员不能声明为static的,只有在外部类或static的内部类中才可声明static成员。

匿名内部类

​ 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

面向对象内容总结

​ 面向对象特性,是java学习的核心、重头戏

面试题

  • abstract class 和 interface 有什么区别? 【基础】
    答:声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,
    并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型
    是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。

    接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。


  • abstract 的 method 是否可同时是static,是否可同时是native,是否可同时是synchronized?
    答:都不能

  • 是否可以从一个static方法内部发出对非static方法的调用?
    答:不可以,如果其中包含对象的method();不能保证对象初始化.

  • 写一个Singleton出来:Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。一般Singleton模式通常有几种种形式:
    第一种形式:定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Singleton {
    private Singleton(){}
       //在自己内部定义自己一个实例,是不是很奇怪?
       //注意这是private 只供内部调用
       private static Singleton instance = new Singleton();
       //这里提供了一个供外部访问本class的静态方法,可以直接访问  
       public static Singleton getInstance() {
         return instance;   
    }
    }

    第二种形式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Singleton { 
      private static Singleton instance = null;
      public static synchronized Singleton getInstance() {
      //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
      //使用时生成实例,提高了效率!
      if (instance==null)
        instance=new Singleton();
    return instance;   
    }
    }

    其他形式:
    定义一个类,它的构造函数为private的,所有方法为static的。一般认为第一种形式要更加安全些 。


  • 接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?

    答案:接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承实体类,但实体类必须不能是如下两种情况之一:
    1,final修饰符修饰的类是不能的
    2,如果此实体类有且仅有私有的构造函数也是不能的。


  • Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)

    答:匿名的内部类是没有名字的内部类。不能extends(继承) 其它类,但一个内部类可以作为一个接口,由另一个内部类实现。


  • Static Nested Class 和 Inner Class的不同
    答:Nested Class (一般是C++的说法),Inner Class (一般是JAVA的说法)。
    Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。
    注: 静态内部类(Inner Class)意味着:
    1 创建一个static内部类的对象,不需要一个外部类对象

    2 不能从一个static内部类的一个对象访问一个外部类对象


  • 谈谈final, finally, finalize的区别
    答:final―修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract 的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载
    finally―再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,
    然后控制就会进入 finally 块(如果有的话)
    finalize―方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
    这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。

  • 如下程序是否可通过编译?

    1
    2
    3
    4
    5
    public class Something {
    public int addOne(final int x) {
    return ++x;
    }
    }

答案: 错。int x被修饰成final,意味着x不能在addOne method中被修改。


1
2
3
4
abstract class Name {
private String name;
public abstract boolean isStupidName(String name) {}
}
  • 这有何错误?
    答案: 错。abstract method必须以分号结尾,且不带花括号。

  • 如下程序是否可通过编译?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Something {
    public static void main(String[] args) {
    Other o = new Other();
    new Something().addOne(o);
    }
    public void addOne(final Other o) {
    o.i++;
    }
    }
    class Other {
    public int i;
    }

    和上面的很相似,都是关于final的问题,这有错吗?
    答案: 正确。在addOne method中,参数o被修饰成final。如果在addOne method里我们修改了o的reference(比如: o = new Other();),那么如同上例这题也是错的。但这里修改的是o的member vairable(成员变量),而o的reference并没有改变。


1
2
3
4
5
6
7
8
9
public class Something {
public static void main(String[] args) {
Something s = new Something();
System.out.println("s.doSomething() returns " + doSomething());
}
public String doSomething() {
return "Do something ...";
}
}

看上去很完美。
答案: 错。看上去在main里call doSomething没有什么问题,毕竟两个methods都在同一个class里。但仔细看,main是static的。static method不能直接call non-static methods。可改成”System.out.println("s.doSomething() returns " + s.doSomething());“。同理,static method不能访问non-static instant variable。


  • 是否可以通过编译?

    1
    2
    3
    abstract class Something {
    private abstract String doSomething ();
    }

答案: 错。abstract的methods不能以private修饰。abstract的methods就是让子类implement(实现)具体细节的,怎么可以用private把abstract method封锁起来呢? (同理,abstract method前不能加final)。


  • 是否可以通过编译?

    1
    2
    3
    4
    5
    6
    class Something {
    int i;
    public void doSomething() {
    System.out.println("i = " + i);
    }
    }

答案: 正确。输出的是”i = 0”。int i属於instant variable (实例变量,或叫成员变量)。instant variable有default value。int的default value是0。


  • 接上题

    1
    2
    3
    4
    5
    6
    class Something {
    final int i;
    public void doSomething() {
    System.out.println("i = " + i);
    }
    }

    和上面一题只有一个地方不同,就是多了一个final。这难道就错了吗?
    答案: 错。final int i是个final的instant variable (实例变量,或叫成员变量)。final的instant variable没有default value,
    必须在constructor (构造器)结束之前被赋予一个明确的值。可以修改为”final int i = 0;”。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface  A{
int x = 0;
}
class B{
int x =1;
}
class C extends B implements A {
public void pX(){
System.out.println(x); //super.x A.x
}
public static void main(String[] args) {
new C().pX();
}
}

答案:错误。在编译时会发生错误(错误描述不同的JVM有不同的信息,意思就是未明确的x调用,两个x都匹配(就象在同时import java.util和java.sql两个包时直接声明Date一样)。对于父类的变量,可以用super.x来明确,而接口的属性默认隐含为 public static final.所以可以通过A.x来明确。


  • 写出程序结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    interface A{

    }
    class B implements A
    {
    public String func()
    {
    return "func";
    }
    }
    class Demo
    {
    public static void main(String[] args)
    {
    A a=new B();
    System.out.println(a.func());
    }
    }

    编译失败:因为A接口中并未定义func方法。


  • 写出程序结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class TD
    {
    int y=6;
    class Inner
    {
    static int y=3;
    void show()
    {
    System.out.println(y);
    }
    }
    }
    class TC
    {
    public static void main(String[] args)
    {
    TD.Inner ti=new TD().new Inner();
    ti.show();
    }
    }

    编译失败,非静态内部类中不可以定义静态成员。内部类中如果定义了静态成员,该内部类必须被静态修饰。


  • 写出程序结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Demo
    {
    private static int j = 0;
    private static boolean methodB(int k)
    {
    j += k;
    return true;
    }
    public static void methodA(int i)
    {
    boolean b;
    b = i < 10 | methodB (4);
    b = i < 10 || methodB (8);
    }
    public static void main (String args[] )
    {
    methodA (0);
    System.out.println(j); //4
    }
    }