Java程序基本结构
/**
* 可以用来自动创建文档的注释
*/
public class Hello {
public static void main(String[] args) {
// 向屏幕输出文本:
System.out.println("Hello, world!");
/* 多行注释开始
注释内容
注释结束 */
}
} // class定义结束
-
class
关键字用来创建类,类名必须以英文字母开头,习惯大写,大驼峰命名。 -
public
是访问修饰符,代表该class
是公开的 -
class
内部可以定义若干方法 -
main
是方法名,void
代表该方法没有返回值,static
是修饰符,表示该方法是静态的,String[] args
表示该方法的参数必须是String数组,args是入参,方法的命名规范一般为小驼峰。 - 在方法内部才是真正执行的代码,
java
的每一行代码必须以;
结束。 -
System.out.println()
是内置的打印方法,结尾自带换行,System.out.print()
方法不换行显示,\t
可以添加若干空格。
数据类型和变量
- java的变量定义方式为
类型 变量名 = 值
- 在Java中,变量分为两种:基本类型的变量和引用类型的变量。
-
基本数据类型
基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:
- 整数类型:
byte,short,int,long
类型 | 占用储存空间 | 表数范围 |
---|---|---|
byte |
1 字节=8bit 位 |
-128~127 |
short | 2 字节 | -32768 ~ 32767 |
int | 4 字节 | -2147483648 ~ 2147483647 |
long | 8 字节 | -9223372036854775808 ~ 9223372036854775807 |
Java程序中变量通常声明为int型,除非不足以表示较大的数,才使用long
long n2 = 900L
long型的结尾需要加L
- 浮点数类型:
float,double
类型 | 占用储存空间 | 表数范围 |
---|---|---|
单精度 float | 4字节 | -3.403E38~3.403E38 |
双精度 double | 8 字节 | -1.798E308~1.798E308 |
(1) float:单精度,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求。
(2) double:双精度,精度是float的两倍。通常采用此类型
(3) 对于float
类型,需要加上f
后缀。
- 字符类型:
char
- char 型数据用来表示通常意义上“字符”(一个字段=2字节,相当于16个bit)
- Java中的所有字符都使用Unicode编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符
- 定义char变量,通常使用一对 ' '(单引号),内部只能写一个字符
- 表达方式:(1)声明一个字符;(2)转义字符
- 直接使用Unicode值来表示字符型常量:"\uXXXX",其中,XXXX代表一个十六进制整数。如:\u000a表示\n。
- 拓展(转义字符):\b(退格符)、\n(换行符)、\r(回车符)、\t(制表符)、"(双引号)、'(单引号)、\(反斜线)
- 布尔类型:
boolean
布尔类型boolean
只有true
和false
两个值
常量
定义变量的时候,如果加上final
修饰符,这个变量就变成了常量:
final double PI = 3.14; // PI是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!
- 常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。为了和变量区分开来,常量名通常全部大写。
移位运算
>>
和 <<
表示又位移和左位移,位移指的是会把十进制数转换成2进制数进行1的位移操作
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14
int b = n << 2; // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912
数组类型
- 数组定义:
类型[] 变量名 = new 类型[数量]
- 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false;
- 数组一旦创建后,大小就不可改变。
条件判断
-
>,<,>=,<=,==
都可以用来作为判断 - 浮点值不能直接用
==
来判断,正确的方法是利用差值小于某个临界值来判断:
// 条件判断
public class Main {
public static void main(String[] args) {
double x = 1 - 9.0 / 10;
if (Math.abs(x - 0.1) < 0.00001) {
System.out.println("x is 0.1");
} else {
System.out.println("x is NOT 0.1");
}
}
}
- 判断引用类型相等
在Java中,判断值类型的变量是否相等,可以使用==运算符。但是,判断引用类型的变量是否相等,==表示“引用是否相等”,或者说,是否指向同一个对象。例如,下面的两个String类型,它们的内容是相同的,但是,分别指向不同的对象,用==判断,结果为false:
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
System.out.println(s1);
System.out.println(s2);
if (s1 == s2) {
System.out.println("s1 == s2");
} else {
System.out.println("s1 != s2");
}
}
}
要判断引用类型的变量内容是否相等,必须使用equals()
方法:
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
System.out.println(s1);
System.out.println(s2);
if (s1.equals(s2)) {
System.out.println("s1 equals s2");
} else {
System.out.println("s1 not equals s2");
}
}
}
注意:执行语句s1.equals(s2)时,如果变量s1为null,会报NullPointerException
类
-
public
定义公共变量, -
private
定义私有变量 -
this
始终指向当前实例,如果没有命名冲突可以省略
class Person {
private String name;
public String getName() {
return name; // 相当于this.name
}
}
- 可变参数
类型...
,可变参数相当于数组类型:
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames("Xiao Ming"); // 传入1个String
g.setNames(); // 传入0个String
可变参数改写为String[]类型
但是,调用方需要自己先构造String[],比较麻烦
另一个问题是,调用方可以传入null:
而可变参数可以保证无法传入null,因为传入0个参数时,接收到的实际值是一个空数组而不是null。
- 实例在创建时通过new操作符会调用其对应的构造方法,构造方法用于初始化实例;
没有定义构造方法时,编译器会自动创建一个默认的无参数构造方法;
可以定义多个构造方法,编译器根据参数自动判断;
可以在一个构造方法内部调用另一个构造方法,便于代码复用。 -
方法重载
,在一个类中,我们可以定义多个方法。如果有一系列方法,它们的功能都是类似的,只有参数有所不同,那么,可以把这一组方法名做成同名方法。这种方法名相同,但各自的参数不同,称为方法重载(Overload)。
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}
注意:方法重载的返回值类型通常都是相同的。
继承
- java使用
extends
关键字来实现继承 - 继承是面向对象编程中非常强大的一种机制,它首先可以复用代码。当我们让Student从Person继承时,Student就获得了Person的所有功能,我们只需要为Student编写新增的功能。
class Person {
private String name;
private int age;
public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
}
class Student extends Person {
// 不要重复name和age字段/方法,
// 只需要定义新增score字段/方法:
private int score;
public int getScore() { … }
public void setScore(int score) { … }
}
注意:子类自动获得了父类的所有字段,严禁定义与父类重名的字段!
- Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类。
- 在Java中,没有明确写extends的类,编译器会自动加上extends Object。所以,任何类,除了Object,都会继承自某个类。
protected
- 继承有个特点,就是子类无法访问父类的private字段或者private方法。例如,Student类就无法访问Person类的name和age字段:
class Person {
private String name;
private int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + name; // 编译错误:无法访问name字段
}
}
这使得继承的作用被削弱了。为了让子类可以访问父类的字段,我们需要把private改为protected。用protected修饰的字段可以被子类访问:
class Person {
protected String name;
protected int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + name; // OK!
}
}
因此,protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问
super
-
super
关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName
- 如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。
这里还顺带引出了另一个问题:即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
阻止继承
- 正常情况下,只要某个
class
没有final
修饰符,那么任何类都可以从该class
继承。
从Java 15开始,允许使用sealed
修饰class
,并通过permits
明确写出能够从该class继承的子类名称。
public sealed class Shape permits Rect, Circle, Triangle {
...
}
上述Shape类就是一个sealed类,它只允许指定的3个类继承它
多态
多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
- 在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。
- 加上@Override可以让编译器帮助检查是否进行了正确的覆写。希望进行覆写,但是不小心写错了方法签名,编译器会报错。
抽象类
-
abstract
关键字可以定义抽象类和抽象方法
abstract class Person {
public abstract void run();
}
抽象类是无法被实例化的,抽象方法也无法执行
接口
-
interface
用来声明接口 - 接口是比抽象类还要抽象的纯抽象接口,如果一个抽象类没有字段全是抽象方法,就可以写成接口