本文假设读者已有其他面向对象高级语言的基础(如C++基础),在此基础上介绍 Java 的基础知识。
目录
1 基本名词解释
JAVA :一种面向对象的高级语言
JDK:Java Development Kit,功能齐全的 Java SDK,能够创建和编译 Java 程序的开发套件;Java 8 对应的 JDK 版本是 1.8
JRE:Java Runtime Environment, Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,主要包括 Java 虚拟机(JVM)、Java 基础类库(Class Library)
JVM:Java 虚拟机(Java Virtual Machine, JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在
jar:Java Archive Tool,一种软件包文件格式,通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便开发Java平台应用软件或库
2 关键字
JAVA中的关键字如下
分类 | 关键字 | |||||||
访问控制 | private | protected | public | |||||
类,方法 和变量修 饰符 | abstract | class | extends | final | implements | interface | native | enum |
new | static | strictfp | synchronized | transient | volatile | |||
程序控制 | break | continue | return | do | while | if | else | assert |
for | instanceof | switch | case | default | ||||
错误处理 | try | catch | throw | throws | finally | |||
包相关 | import | package | ||||||
基本类型 | boolean | byte | char | double | float | int | long | short |
变量引用 | super | this | void | |||||
保留字 | goto | const |
接下来分别介绍上述关键字
Java 中的访问控制符
-
public:成员可以在任何地方被访问,包括同一包(Package)、不同包中的子类和其他类。
-
protected:
- 成员可以在同一包内被访问,也可以被不同包中的子类访问。
- 即
protected
成员对所有子类都可见,不论子类是否在同一包中。
-
default(无访问修饰符):
- 成员仅在同一包内可见。这有时被称为包访问权限。
-
private:
- 成员仅在声明它的类内部可见。这是最严格的访问级别。
在Java中,继承总是 public
继承,所以 protected
成员总是对子类可见,而 private
成员则始终不可见。
Java 中的包
在Java中,包(Package)是一种组织类和接口的方式,它可以帮助开发者管理类名空间,避免命名冲突,并控制类的可见性。
什么是包?
- 包是一组相关的类和接口的集合。
- 包通过使用
package
关键字来声明。 - 包名通常采用反向域名的形式,例如
com.example.myapp
。 - 包可以嵌套在其他包中形成层次结构。
同一个包内
- 同一个包内指的是那些具有相同包声明的类或接口。这意味着它们位于文件系统的同一目录下,并且它们的源文件都包含相同的
package
语句。 - 在同一个包内的类可以互相访问默认访问级别的成员(即没有明确指定
public
、protected
或private
的成员)。
包的作用
- 避免命名冲突:通过使用不同的包名,可以确保不同项目中的类名即使相同也不会发生冲突。
- 访问控制:包提供了一种方式来控制类和成员的可见性。例如,
protected
和默认访问级别的成员只能在同一个包内的类中访问。 - 导入类:使用
import
语句可以方便地引用其他包中的类。
如何声明包
在Java源文件的顶部,你可以使用package
关键字来声明一个包:
package com.example.myapp;
public class MyClass {
// 类的定义
}
文件结构
假设你的文件系统结构如下:
src/
|-- com/
| |-- example/
| |-- myapp/
| |-- PackageExample.java
| |-- AnotherClass.java
PackageExample.java
package com.example.myapp;
public class PackageExample {
// 默认访问级别的变量
String message = "Hello from PackageExample!";
public void displayMessage() {
System.out.println(message);
}
}
AnotherClass.java
package com.example.myapp;
public class AnotherClass {
public static void main(String[] args) {
PackageExample example = new PackageExample();
System.out.println(example.message); // 访问同一个包内的默认访问级别的成员
example.displayMessage();
}
}
在这个示例中,PackageExample
和AnotherClass
都在com.example.myapp
包内。因此,AnotherClass
可以访问PackageExample
中的默认访问级别的成员message
。
要编译和运行这些类,你需要确保它们位于正确的目录结构中,以匹配包声明。例如,如果你使用的是命令行编译器,你可以这样编译和运行:
# 编译
javac -d build src/com/example/myapp/PackageExample.java
javac -d build src/com/example/myapp/AnotherClass.java
# 运行
java -cp build com.example.myapp.AnotherClass
这里,-d build
指定了编译后的.class
文件的输出目录,-cp build
设置了类路径。
编译运行后控制台会输出如下结果
Hello from PackageExample!
Hello from PackageExample!
import 关键字用于导入包的内容,有几种常用的导入方法:
- 导入单个类:import java.util.Date; // 关键字后跟完整的类名(包括包名)
- 导入整个包:import java.util.*; // 包后面用*
- 导入静态成员:import static java.lang.Math.PI; // 加上 static 关键字
类与继承
- static:声明静态成员(包括类内的变量和函数),静态成员在类加载得到时候就已经被分配内存已经存在了(而不是等到实例化的时候才有),且只在类初始化时分配一次内存(而不是每个类分配一次),所有类的实例都可以访问静态成员,静态成员可以直接通过类名调用(无需实例化)
- new:实例化一个新对象,在 Java 中使用 new 来声明一个新对象会自动调用构造函数(并在使用完后自动调用析构函数)
- abstract:用于声明抽象类或抽象方法,抽象类或抽象方法需在子类中强制重写
- class:声明一个类
- extends:MyClassA extends MyClassB 表示 MyClassA 继承类 MyClassB ,注意 Java 中只允许单继承,不允许多继承
- implements:implements 是用于实现接口的关键字。当一个类需要实现一个或多个接口时,会使用 implements 关键字。接口只包含方法签名(没有方法体),并且可以包含常量。实现接口的类必须提供接口中所有方法的具体实现,除非该类本身是抽象类。例如:
interface CanEat {
void eat();
}
class Animal {}
class Dog extends Animal implements CanEat {
@Override
public void eat() {
System.out.println("The dog is eating.");
}
}
上述例子中,Dog
类继承了Animal
类并实现了CanEat
接口,因此它必须提供eat()
方法的实现。
总结来说,extends
用于继承类,支持单继承(一个类只能有一个直接父类),而implements
用于实现接口,支持多重实现(一个类可以实现多个接口)。
这两种机制都允许代码复用,但以不同的方式实现。类继承更多地用于结构和行为的复用,而接口实现则用于定义行为的规范。
- interface:要使用 implements 关键字继承的抽象父类,示例代码见下 default。
- default:从 Java8 开始,在使用 interface 声明的抽象父类中,可以有函数有默认的实现方法(在子类的接口中可以不用重写 dafault 方法,要重写的话要加上 @Override),例如:
现有提供的 Drawable
接口中定义了一个 showInfo()
方法:
public interface Drawable {
void draw(); // 抽象方法
default void showInfo() {
System.out.println("This is a drawable object.");
}
}
这里 showInfo()
方法是默认方法,它有一个具体的实现。任何实现了 Drawable
接口的类都会自动获得这个方法的new实现,除非该类明确地覆盖(@Override)了这个方法。
public class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a circle.");
}
}
public class Square implements Drawable {
// 如果 Square 类要继承自多个父类,则用 “,” 分开
// 如 public class Square implements Drawable, Resize {
@Override
public void draw() {
System.out.println("Drawing a square.");
}
// 如果需要覆盖默认方法,可以这样做:
@Override
public void showInfo() {
System.out.println("This is a square.");
}
}
在这个例子中,Circle
类没有覆盖 showInfo()
方法,因此它会使用 Drawable
接口中定义的默认实现。而 Square
类覆盖了 showInfo()
方法,提供了自己的实现。
default
关键字提供向后兼容性,允许在现有接口中添加新方法而不破坏现有的实现。
- final:用 final 关键字修饰的类、方法和变量不可再继承、重写和修改;但如果
final
变量引用的是一个对象,那么final
关键字保证的是引用不会改变,而不是对象的内容。也就是说,你不能改变final
变量指向的引用,但如果你的final
变量引用的是一个可变对象(如ArrayList
),你仍然可以改变对象内部的状态。例如:
final List<String> list = new ArrayList<>();
list.add("Item"); // 这是允许的
其他变量修饰符
native:native
关键字用于声明一个方法是由本地其他代码(如C或C++)实现的,而不是由Java代码实现的。native
关键字通常与public
、protected
、private
等访问修饰符一起使用,但不能与final
、abstract
、synchronized
、strictfp
等关键字一起使用,因为它们与本地方法的概念不兼容。strictfp:该
关键字用于确保浮点数运算的精度和一致性。它主要用于限制浮点数运算的执行模式,以确保跨平台的结果一致性。synchronized
:该关键字通过锁机制实现线程同步,以确保多个线程之间的互斥访问,防止数据竞争条件的发生。synchronized
可以用于方法或者代码块,确保同一时刻只有一个线程能够执行被同步的代码。transient
:用于标记类中的成员变量,表明该变量不应该被序列化。序列化是将对象的状态转换为字节流的过程,以便可以存储或在网络上传输。volatile:
修饰的变量每次读都会从主内存(Main Memory)中加载该变量的最新值,而不是从线程的工作内存(Working Memory)中读取可能已过期的副本。这样当主线程改变volatile修饰的变量
值时,正在运行的任务线程会立即看到这个变化。但volatile 并不保证操作的原子性,复杂的操作可能还是需要 synchronized 进行。
enum:enum即
枚举是一种特殊的类,用于定义一组固定的常量,通常代表有限集合中的元素。枚举提供了一种类型安全的方式来表示一组相关的常量,而不是使用整数或字符串。更详细的示例可见Java 枚举(超详细讲解)
程序控制
break, continue, return, do, while, if, else, for, switch, case, default 都和 C++ 中的类似
- instanceof:判断某个实例是否是某个类或其子类,示例如下
要检查的实例 instanceof 类名
- assert:断言,假设此处应该满足某种条件,不然会抛出什么错误,示例如下
assert <布尔表达式> : <可选的错误信息>;
错误处理
用下列代码说明 Java 中的 try, catch, throw, throws 和finally 关键字
public class ExceptionDemo {
public static void main(String[] args) {
try {
// 尝试执行可能会抛出异常的代码
performAction();
} catch (ArithmeticException e) {
// 处理特定类型的异常
System.out.println("Caught an ArithmeticException: " + e.getMessage());
} catch (Exception e) {
// 处理其他类型的异常
System.out.println("Caught another exception: " + e.getMessage());
} finally {
// 总是执行的代码,无论是否有异常发生
System.out.println("Finally block executed.");
}
}
public static void performAction() throws IOException {
try {
// 抛出一个异常
throw new ArithmeticException("Divide by zero error.");
} catch (ArithmeticException e) {
// 在方法内部处理异常,然后再次抛出
System.out.println("Handling ArithmeticException in performAction()");
throw e;
} finally {
// 清理资源或执行必要的关闭操作
System.out.println("Cleaning up resources in performAction().");
}
}
}
其中 throws
关键字表明 performAction() 方法可能会抛出 IOException 异常。当你在方法签名中使用 throws
关键字时,你告诉调用者(或其他方法)这个方法在执行过程中可能会抛出特定类型的异常,调用者应该准备好处理这些异常,要么通过自己的 try-catch
块捕获它们,要么继续在调用链中声明这些异常。
throws
关键字后跟一个或多个异常类的名称,这些异常类应该是具体的异常类型,通常是 Exception
类或其子类。
基本类型
Java中的基本数据类型(Primitive Data Types)是直接存储值的类型,它们不是对象,也不需要在堆上分配内存。Java提供了八种基本数据类型,分为四类:
-
整型 (Integer Types)
byte
: 占用1个字节(8位),范围从-128到127。short
: 占用2个字节(16位),范围从-32,768到32,767。int
: 占用4个字节(32位),范围从-2,147,483,648到2,147,483,647。long
: 占用8个字节(64位),范围从-9,223,372,036,854,775,808到9,223,372,036,854,775,807。
-
浮点型 (Floating Point Types)
float
: 占用4个字节(32位),可以表示小数,有效数字约为7位。double
: 占用8个字节(64位),可以表示小数,有效数字约为15位,精度高于float
。
-
字符型 (Character Type)
char
: 占用2个字节(16位),用于存储Unicode字符。
-
布尔型 (Boolean Type)
boolean
: 只有两个值,true
和false
,不占用固定的字节数,但通常被实现为占1个字节。
Java 中每种基本类型都有对应的包装类,如Integer
对应int
,Double
对应double
等,这使得基本类型可以作为对象使用,从而支持一些面向对象的特性,如方法调用和异常处理。
变量引用
- super:
在Java中,super 关键字用于引用当前对象的直接父类的对象。它可以在子类中调用父类的构造函数、父类普通函数,访问父类的字段或其成员变量,示例代码如下:
public class Child extends Parent { public Child() { super(); // 调用Parent类的无参构造器 } public void someMethod() { super.someMethod(); // 子类重写父类的方法,仍可调用父类被重写的方法 // 其他代码... } // 如果子类和父类中有同名的字段,可用super.field来访问父类中的字段 private int field; // 子类也有一个field字段 public void printField() { System.out.println(super.field); // 访问Parent类的field字段 } // 在子类的构造函数中初始化父类的成员变量,这时可以使用super private String childProperty; public Child(String parentProperty, String childProperty) { super(parentProperty); // 初始化Parent类的成员变量 this.childProperty = childProperty; } }
super 关键字不能在静态方法或静态字段中使用,因为静态成员与类相关联,而不是与类的实例相关联。因此,它们不能访问非静态的父类成员。
-
this:指向本类
-
void:表示本函数不返回任何东西
保留字
- goto:Java 中避免使用 goto,可以使用 break 和 continue 来跳出多重循环
- const:Java 中避免使用 const,可以使用 final 来实现常量