Java Review

变量和数据类型

Java提供了两种变量类型:基本类型和引用类型。基本类型包括整型,浮点型,布尔型,字符型。变量可重新赋值,等号是赋值语句,不是数学意义的等号。常量在初始化后不可重新赋值,使用常量便于理解程序意图。

基本数据类型

  • 整数类型:byte,short,int,long
  • 浮点数类型:float,double
  • 字符类型:char
  • 布尔类型:boolean
数据类型 大小(二进制位数) 范围 默认值 包装类
byte(字节) 8 -128 - 127 0 Integer
shot(短整型) 16 -32768 - 32768 0 Short
int(整型) 32 -2147483648-2147483648 0 Long
long(长整型) 64 -9233372036854477808-9233372036854477808 0 Byte
float(浮点型) 32 -3.40292347E+38-3.40292347E+38 0.0f Float
double(双精度) 64 -1.79769313486231570E+308-1.79769313486231570E+308 0.0d Double
char(字符型) 16 \\u0000 - u\\ffff \\u0000 Character
boolean(布尔型) 1 true/false false Boolean

Java有一个能够表示任意精度的算书包,通常称为大数值(big number)。虽然被称为大数值,但它并不是一种Java类型,而是一个Java对象。
如果基本的整数和浮点数精度不能够满足需求,那么可以使用java.math包中的两个很有用的类:BigInteger,BigDecimal(Android SDK中也包含了java.math包以及这两个类)这两个类可以处理包含任意长度数字序列的数值。BigInteger类实现了任意精度的整数运算,BigDecimal实现了任意精度的浮点数运算

常量

定义变量的时候,如果加上final修饰符,这个变量就变成了常量。常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。

1
final double PI = 3.14

var关键字

有些时候,类型的名字太长,写起来比较麻烦。这个时候,如果想省略变量类型,可以使用var关键字,编译器会根据赋值语句自动推断出变量sb的类型是StringBuilder。

1
2
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();

引用类型

数组类型 定义一个数组类型的变量,使用数组类型类型[],例如,int[]。和单个基本类型变量不同,数组变量初始化必须使用new int[5]表示创建一个可容纳5个int元素的数组。

  • 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false;
  • 数组一旦创建后,大小就不可改变。
1
2
int[] ns = new int[5];
int[] ns = new int[] { 68, 79, 91, 85, 62 };

字符串数组

如果数组元素不是基本类型,而是一个引用类型,那么,修改数组元素会有哪些不同?

1
2
3
String[] names = {
"ABC", "XYZ", "zoo"
};

变量的作用范围

在Java中,多行语句用{ }括起来。很多控制语句,例如条件判断和循环,都以{ }作为它们自身的范围。而在语句块中定义的变量,它有一个作用域,就是从定义处开始,到语句块结束。

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
if (...) { // if开始
...
while (...) { while 开始
...
if (...) { // if开始
...
} // if结束
...
} // while结束
...
} // if结束


{
...
int i = 0; // 变量i从这里开始定义
...
{
...
int x = 1; // 变量x从这里开始定义
...
{
...
String s = "hello"; // 变量s从这里开始定义
...
} // 变量s作用域到此结束
...
// 注意,这是一个新的变量s,它和上面的变量同名,
// 但是因为作用域不同,它们是两个不同的变量:
String s = "hi";
...
} // 变量x和s作用域到此结束
...
} // 变量i作用域到此结束

修饰符

Java语言提供了很多修饰符,主要分为以下两类:

  • 访问修饰符
  • 非访问修饰符

访问修饰符

修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
public Y Y Y Y Y
protected Y Y Y Y/N(说明) N
default Y Y Y N N
private Y N N N N

非访问修饰符

  1. static 修饰符,用来修饰类方法和类变量。

静态变量: static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。对类变量和方法的访问可以直接使用 classname.variablename 和 classname.methodname 的方式访问。
静态方法: 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
public class InstanceCounter {
private static int numInstances = 0;
protected static int getCount() {
return numInstances;
}

private static void addInstance() {
numInstances++;
}

InstanceCounter() {
InstanceCounter.addInstance();
}

public static void main(String[] arguments) {
System.out.println("Starting with " +
InstanceCounter.getCount() + " instances");
for (int i = 0; i < 500; ++i){
new InstanceCounter();
}
System.out.println("Created " +
InstanceCounter.getCount() + " instances");
}
}
  1. final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
  2. abstract 修饰符,用来创建抽象类和抽象方法。
  3. synchronized 和 volatile 修饰符,主要用于线程的编程。

运算

算术运算符

操作符 描述 A=10,B=20
+ 加法: 相加运算符两侧的值 A + B 等于 30
- 减法: 左操作数减去右操作数 A – B 等于 -10
* 乘法: 相乘操作符两侧的值 A * B等于200
/ 除法: 左操作数除以右操作数 B / A等于2
取余: 左操作数除以右操作数的余数 B%A等于0
++ 自增: 操作数的值增加1 B++ 或 ++B 等于 21
自减: 操作数的值减少1 B— 或 —B 等于 19

关系运算符

运算符 描述 例子
== 检查如果两个操作数的值是否相等,如果相等则条件为真。 (A == B)为假。
!= 检查如果两个操作数的值是否相等,如果值不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是那么条件为真。 (A> B)为假。
< 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。 (A <B)为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 (A> = B)为假。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 (A <= B)为真。

位运算符

运算符 描述 例子
如果相对应位都是1,则结果为1,否则为0 (A&B),得到12,即0000 1100
\ 如果相对应位都是0,则结果为0,否则为1 (A \ B)得到61,即 0011 1101
^ 如果相对应位值相同,则结果为0,否则为1 (A ^ B)得到49,即 0011 0001
按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 (〜A)得到-61,即1100 0011
<< 按位左移运算符。左操作数按位左移右操作数指定的位数。 A << 2得到240,即 1111 0000
>> 按位右移运算符。左操作数按位右移右操作数指定的位数。 A >> 2得到15即 1111
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 A>>>2得到15即0000 1111

短路逻辑运算符

当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了。

1
2
3
4
5
6
7
8
public class LuoJi{
public static void main(String[] args){
int a = 5;//定义一个变量;
boolean b = (a<4)&&(a++<10);
System.out.println("使用短路逻辑运算符的结果为"+b);
System.out.println("a的结果为"+a);
}
}

逻辑运算符

运算符 描述 例子
&& 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 (A && B)为假。}
\ \ 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 (A \ \ B)为真。}
称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 !(A && B)为真。}

条件运算符(?:)

1
var x = (expression) ? value if true : value if false

instanceof 运算符

如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。

1
2
3
4
( Object reference variable ) instanceof  (class/interface type)

String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真

类型自动提升与强制转型

  • 在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型。例如,short和int计算,结果总是int,原因是short首先自动被转型为int
  • 如果参与运算的两个数其中一个是整型,那么整型可以自动提升到浮点型。

    1
    2
    int n = 5;
    double d = 1.2 + 24.0 / n; // 6.0
  • 可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉。如果转型后超过了整型能表示的最大范围,将返回整型的最大值。

    1
    int n4 = (int) 1.2e20; // 2147483647

字符和字符串

字符类型

char是基本数据类型,它是character的缩写。一个char保存一个Unicode字符。因为Java在内存中总是使用Unicode表示字符,所以,一个英文字符和一个中文字符都用一个char类型表示,它们都占用两个字节

1
2
char c1 = 'A';
char c2 = '中';

要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可:

1
2
int n1 = 'A'; // 字母“A”的Unicodde编码是65
int n2 = '中'; // 汉字“中”的Unicode编码是20013

还可以直接用转义字符\u+Unicode编码来表示一个字符:

1
2
char c3 = '\u0041'; // 'A',因为十六进制0041 = 十进制65
char c4 = '\u4e2d'; // '中',因为十六进制4e2d = 十进制20013

字符串类型

字符串类型String是引用类型,使用双引号”…”表示开始和结束。可以使用+连接任意字符串和其他数据类型。

1
2
3
4
5
6
7
String s = ""; // 空字符串,包含0个字符
String s1 = "A"; // 包含一个字符
String s2 = "ABC"; // 包含3个字符
String s3 = "中文 ABC"; // 包含6个字符,其中有一个空格

int age = 25;
String s = "age is " + age;

字符串不可变

空值null

引用类型的变量可以指向一个空值null,它表示不存在,即该变量不指向任何对象。注意要区分空值null和空字符串””,空字符串是一个有效的字符串对象,它不等于null

1
String s1 = null; // s1是null

流程控制

格式化输出

1
2
3
System.out.printf("%.2f\n", d); // 显示两位小数3.14
System.out.printf("%.4f\n", d); // 显示4位小数3.1416
System.out.printf("n=%d, hex=%08x", n, n); // 注意,两个%占位符必须传入两个数
占位符 说明
%d 格式化输出整数
%x 格式化输出十六进制整数
%f 格式化输出浮点数
%e 格式化输出科学计数法表示的浮点数
%s 格式化字符串

输入

首先,我们通过import语句导入java.util.Scanner,import是导入某个类的语句,必须放到Java源代码的开头,后面我们在Java的package中会详细讲解如何使用import。然后,创建Scanner对象并传入System.in。System.out代表标准输出流,而System.in代表标准输入流。直接使用System.in读取用户输入虽然是可以的,但需要更复杂的代码,而通过Scanner就可以简化后续的代码。有了Scanner对象后,要读取用户输入的字符串,使用scanner.nextLine(),要读取用户输入的整数,使用scanner.nextInt()。Scanner会自动转换数据类型,因此不必手动转换。

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建Scanner对象
System.out.print("Input your name: "); // 打印提示
String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.print("Input your age: "); // 打印提示
int age = scanner.nextInt(); // 读取一行输入并获取整数
System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
}
}

if判断

1
2
3
4
5
if (条件) {  // (true or false)
// 条件为true满足时执行
}else {
// 条件为false满足时执行
}

判断引用类型相等

判断值类型的变量是否相等,可以使用==运算符。但是,判断引用类型的变量是否相等,==表示引用是否相等,或者说,是否指向同一个对象。

1
2
3
4
5
6
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
if (s1 == s2) {
System.out.println("s1 == s2");
} else {
System.out.println("s1 != s2"); // s1,s2引用不同的对象

要判断引用类型的变量内容是否相等,必须使用equals()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
if (s1.equals(s2)) {
System.out.println("s1 == s2"); // s1,s2 值相等
} else {
System.out.println("s1 != s2");

//要避免NullPointerException错误,可以利用**短路运算符&&**:
```Java
public class Main {
public static void main(String[] args) {
String s1 = null;
if (s1 != null && s1.equals("hello")) {
System.out.println("hello");
}
}
}

switch多重选择

switch语句根据switch (表达式)计算的结果,跳转到匹配的case结果,然后继续执行后续语句,直到遇到break结束执行。可以给switch语句加一个default,当没有匹配到任何case时,执行default。

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 class Main {
public static void main(String[] args) {
int option = 99;
switch (option) {
case 1:
System.out.println("Selected 1");
break;
case 2:
System.out.println("Selected 2");
break;
case 3:
System.out.println("Selected 3");
break;
default:
System.out.println("Not selected");
break;
}
}
}

// if (option == 1) {
// System.out.println("Selected 1");
// } else if (option == 2) {
// System.out.println("Selected 2");
// } else if (option == 3) {
// System.out.println("Selected 3");
// } else {
// System.out.println("Not selected");
// }

循环

while 循环 与 do… while 循环

1
2
3
4
5
6
7
while (条件表达式) {
循环语句
}

do {
执行循环语句
} while (条件表达式);

for 循环

for循环的功能非常强大,它使用计数器实现循环。for循环会先初始化计数器,然后,在每次循环前检测循环条件,在每次循环后更新计数器。计数器变量通常命名为i。for循环还可以缺少初始化语句、循环条件和每次循环更新语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (初始条件; 循环检测条件; 循环后更新计数器) {
// 执行语句
}

// 不设置结束条件:
for (int i=0; ; i++) {
...
}

// 不设置结束条件和更新语句:
for (int i=0; ;) {
...
}

// 什么都不设置:
for (;;) {
...
}

Java还提供了另一种for each循环,它可以更简单地遍历数组。for each循环能够遍历所有“可迭代”的数据类型,包括后面会介绍的List、Map等。

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int i=?; ?; ?) {
System.out.println(ns[i]);
}
}
}

break and continue

  1. 在循环过程中,可以使用break语句跳出当前循环。
  2. continue可以提前结束本次循环,直接继续执行下次循环。

对象和类

Java作为一种面向对象语言。支持以下基本概念:

  • 多态
  • 继承
  • 封装
  • 抽象
  • 对象
  • 实例
  • 方法
  • 重载

类可以看成是创建Java对象的模板。

  1. 一个类可以包含以下类型变量:
  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为static类型。
  1. 一个类可以拥有多个方法,在上面的例子中:barking()、hungry()和sleeping()都是Dog类的方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Dog{
    String breed;
    int age;
    String color;
    void barking(){
    }

    void hungry(){
    }

    void sleeping(){
    }
    }

对象构造和初始化

  • 声明:声明一个对象,包括对象名称和对象类型。
  • 实例化:使用关键字new来创建一个对象。
  • 初始化:使用new创建对象时,会调用构造方法初始化对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Puppy{
public Puppy(){
}

public Puppy(String name){
// 这个构造器仅有一个参数:name
}
}

public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}

实例变量和调用成员方法

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
public class Puppy{
int puppyAge;
public Puppy(String name){
// 这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}

public void setAge( int age ){
puppyAge = age;
}

public int getAge( ){
System.out.println("小狗的年龄为 : " + puppyAge );
return puppyAge;
}

public static void main(String[] args){
/* 创建对象 */
Puppy myPuppy = new Puppy( "tommy" );
/* 通过方法来设定age */
myPuppy.setAge( 2 );
/* 调用另一个方法获取age */
myPuppy.getAge( );
/*你也可以像下面这样访问成员变量 */
System.out.println("变量值 : " + myPuppy.puppyAge );
}
}

继承

1
2
3
4
5
class 父类 {
}

class 子类 extends 父类 {
}

继承类型

继承的特性

  1. 子类拥有父类非 private 的属性、方法。
  2. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  3. 子类可以用自己的方式实现父类的方法。

继承关键字

继承可以使用 extendsimplements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。

  1. extends关键字 在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Animal { 
    private String name;
    private int id;
    public Animal(String myName, String myid) {
    //初始化属性值
    }
    public void eat() { //吃东西方法的具体实现 }
    public void sleep() { //睡觉方法的具体实现 }
    }

    public class Penguin extends Animal{
    }
  2. implements关键字 使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。

1
2
3
4
5
6
7
8
9
10
11
public interface A {
public void eat();
public void sleep();
}

public interface B {
public void show();
}

public class C implements A,B {
}
  1. super 与 this 关键字
  • super:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
  • this:指向自己的引用。
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
class Animal {
void eat() {
System.out.println("animal : eat");
}
}

class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}

public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}


// animal : eat
// dog : eat
// animal : eat
  1. final关键字 final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写。

    1
    2
    final class 类名 {类体}
    修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
  2. 子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

重写(Override)与重载(Overload)

重写(Override)规则

  • 参数列表必须完全与被重写方法的相同;
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同);
    访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为final的方法不能被重写。
  • 声明为static的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个方法,则不能重写这个方法。
  • 当需要在子类中调用父类的被重写方法时,要使用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
25
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}

class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
public void bark(){
System.out.println("狗可以吠叫");
}
}

public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象

a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
b.bark();
}
}

重载(Overload)规则

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。
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 class Overloading {
public int test(){
System.out.println("test1");
return 1;
}

public void test(int a){
System.out.println("test2");
}

//以下两个参数类型顺序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}

public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}

public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}

重写与重载之间的区别

区别点 重载方法 重写方法
参数列表 必须修改 一定不能修改
返回类型 可以修改 一定不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)

多态

现实中,比如我们按下 F1 键这个动作:
如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
如果当前在 Word 下弹出的就是 Word 帮助;
在 Windows 下弹出的就是 Windows 帮助和支持。
同一个事件发生在不同的对象上会产生不同的结果

多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象
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
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法

Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}

public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}

abstract class Animal {
abstract void eat();
}

class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}

class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}

抽象类与抽象方法

  1. 如果一个类包含抽象方法,那么该类必须是抽象类。
  2. 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
  3. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  4. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  5. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  6. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
1
2
3
4
5
6
7
8
9
10
public abstract class Employee
{
private String name;
private String address;
private int number;

public abstract double computePay();

//其余代码
}

封装

  1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:

    1
    2
    3
    4
    public class Person {
    private String name;
    private int age;
    }
  2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
    public class Person{
    private String name;
    private int age;

    public int getAge(){
    return age;
    }

    public String getName(){
    return name;
    }

    public void setAge(int age){
    this.age = age;
    }

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

接口

  1. 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
    除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
  2. 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
1
2
3
4
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}

接口与类的区别:

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

抽象类和接口的区别

  • 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  • 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

源文件声明规则

  1. 一个源文件中只能有一个public类
  2. 一个源文件可以有多个非public类
  3. 源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。
  4. 如果一个类定义在某个包中,那么package语句应该在源文件的首行。
  5. 如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
  6. import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

Java包

包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。

1
package pkg1[.pkg2[.pkg3…]];
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
/* 文件名: Animal.java */
package animals;

interface Animal {
public void eat();
public void travel();
}



// -------------------------

/* 文件名 : MammalInt.java */
package animals;
public class MammalInt implements Animal{

public void eat(){
System.out.println("Mammal eats");
}

public void travel(){
System.out.println("Mammal travels");
}

public int noOfLegs(){
return 0;
}

public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}

Import语句

在Java中,如果给出一个完整的限定名,包括包名、类名,那么Java编译器就可以很容易地定位到源代码或者类。Import语句就是用来提供一个合理的路径,使得编译器可以找到某个类。

1
import package1[.package2…].(classname|*);
Donate article here