Java 基础语法
Java 基础语法
- Java 基础语法
- 1. 名词介绍
- 2. 第一个Java程序
- 3. 基本语法
- 4. Java 标识符
- 5. Java修饰符
- 6. Java 变量
- 7. Java 数组
- 8. Java 枚举
- 9. Java 关键字
- 10. Java注释
- 11. Java 空行
- 12. 继承
- 13. 接口
- 14. Java 源程序与编译型运行区别
1. 名词介绍
一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。
对象 实例
对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,
+ 它的状态有:颜色、名字、品种;
+ 行为有 :摇尾巴、叫、吃等。
类 模板
- 类:类是一个模板,它描述一类对象的行为和状态。
方法 行为
- 方法:方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
实例变量 状态
- 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
类和对象是面向对象编程中的两个核心概念,它们之间有一定的关系,但并不是类是抽象的对象。
-
类(Class):类是一种抽象数据类型,用来描述具有相同属性和行为的对象的集合。类定义了对象的状态(属性)和行为(方法),是对象的模板或蓝图。在Java中,类由属性(成员变量)和方法(成员函数)组成。
-
对象(Object):对象是类的实例,是具体存在的、具有特定状态和行为的实体。可以将类看作是对象的模板,而对象是类的实际实例化。
因此,类是描述对象的模板,而对象是类的具体实例。类是对对象进行抽象和定义的,而对象是类的具体化和实例化的结果。在面向对象编程中,通过定义类来创建对象,从而实现对现实世界中事物的抽象和建模。
2. 第一个Java程序
下面看一个简单的 Java 程序,它将输出字符串 Hello World
实例
public class HelloWorld { /* 第一个Java程序 * 它将输出字符串 Hello World */
public static void main(String[] args) {
System.out.println("Hello World"); // 输出 Hello World
}
}
这是一个简单的 Java 程序,它包含一个名为 HelloWorld
的类,其中包含一个名为 main
的方法。让我们逐步分析这个程序:
-
类声明:程序从
public class HelloWorld {
开始。这行代码定义了一个名为HelloWorld
的公共类。 -
注释:在类声明之后,有一段注释
/* 第一个Java程序 * 它将输出字符串 Hello World */
。这是一个多行注释,用于描述程序的作用。 -
main 方法:在类的内部,定义了一个名为
main
的方法。这是 Java 程序的入口点,程序从这里开始执行。方法的声明是public static void main(String[] args)
。public
表示该方法是公共的,可以被其他类访问。static
表示该方法是静态的,可以直接通过类名调用,而不需要创建类的实例。void
表示该方法没有返回值。main
是方法的名称。(String[] args)
是方法的参数列表,它接受一个字符串数组作为参数。这个数组用于接收命令行参数。
-
输出语句:在
main
方法中,有一行代码System.out.println("Hello World");
。这是一个输出语句,用于在控制台打印字符串 “Hello World”。 -
花括号:在 Java 中,代码块使用花括号
{}
来定义。在这个程序中,类的定义和方法的定义都被花括号包裹起来。
参数 String[] args
在 Java 中通常用于接收命令行参数。在执行 Java 程序时,你可以通过命令行输入参数,这些参数会传递给程序,并存储在 args
数组中。
为什么是 String[]
类型呢?
-
命令行参数是字符串:在命令行中输入的参数通常是字符串形式的。例如,你可以在命令行中输入
java HelloWorld arg1 arg2
,其中arg1
和arg2
就是字符串类型的参数。 -
数组用于存储多个参数:
args
参数是一个字符串数组,它可以存储多个参数。每个参数都被存储在数组的一个元素中。例如,args[0]
存储第一个参数,args[1]
存储第二个参数,依此类推。 -
灵活性:通过命令行参数,可以在执行程序时动态地传递不同的参数值,从而实现程序的灵活性和通用性。这使得程序可以根据不同的需求执行不同的操作。
因此,String[] args
参数在 Java 中用于接收命令行输入的参数,以便程序根据这些参数执行相应的操作。
参数为什么要 为 String[] args
在Java中,main函数的参数为String类型的数组String[] args
是为了接收命令行参数(command-line arguments)。这是为了使Java程序能够与命令行交互,从而实现程序的灵活性和通用性。
通过命令行参数,可以在执行程序时动态地传递不同的参数值,从而实现程序的灵活性和通用性。例如,你可以在命令行中输入java YourProgram arg1 arg2
,其中arg1
和arg2
就是传递给程序的参数,它们会被存储在args数组中。程序可以根据这些参数执行相应的操作,从而实现不同的功能。
因此,main函数的参数为String类型的数组是为了接收命令行输入的参数,以便程序根据这些参数执行相应的操作。
整个程序的功能很简单,就是在控制台输出 “Hello World”。这是一个经典的入门示例,用于展示 Java 程序的基本结构和语法。
下面将逐步介绍如何保存、编译以及运行这个程序:
- 打开代码编辑器,把上面的代码添加进去;
- 把文件名保存为:HelloWorld.java;
- 打开 cmd 命令窗口,进入目标文件所在的位置,假设是 C:\
- 在命令行窗口输入 javac HelloWorld.java 按下回车键编译代码。如果代码没有错误,cmd 命令提示符会进入下一行(假设环境变量都设置好了)。
- 再键输入 java HelloWorld 按下回车键就可以运行程序了
你将会在窗口看到 Hello World
$ javac HelloWorld.java
$ java HelloWorld
Hello World
如果遇到编码问题,我们可以使用 -encoding 选项设置 utf-8 来编译:
javac -encoding UTF-8 HelloWorld.java
java HelloWorld
Gif 图演示:
3. 基本语法
编写 Java 程序时,应注意以下几点:
- 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。
- 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的 Java 程序由 public static void main(String[] args) 方法开始执行。
4. Java 标识符
Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
关于 Java 标识符,有以下几点需要注意:
- 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
- 关键字不能用作标识符
- 标识符是大小写敏感的
- 合法标识符举例:age、$salary、_value、__1_value
- 非法标识符举例:123abc、-salary
5. Java修饰符
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
- 访问控制修饰符 : default, public , protected, private
- 非访问控制修饰符 : final, abstract, static, synchronized
在后面的章节中我们会深入讨论 Java 修饰符。
6. Java 变量
Java 中主要有如下几种类型的变量
- 局部变量
- 类变量(静态变量)
- 成员变量(非静态变量)
是的,Java中主要有以下几种类型的变量:
-
局部变量:局部变量是定义在方法、构造函数或语句块中的变量,它们只在其所属的方法、构造函数或语句块中可见。局部变量必须在声明后才能使用,并且在方法、构造函数或语句块执行完毕后会被销毁。
-
类变量(静态变量):类变量,也称为静态变量,是用static关键字修饰的变量,它们属于类而不是任何单个的实例。类变量在整个类加载过程中只会被初始化一次,并且所有类的实例共享同一个类变量的值。
-
成员变量(非静态变量):成员变量,也称为实例变量或非静态变量,是定义在类中但在方法之外的变量。每个类的实例都有自己的一组成员变量的副本,它们与特定实例相关联,每个实例都有自己的成员变量的值。
局部变量 类变量(静态变量) 成员变量(非静态变量)
代码表现
好的,让我用代码示例来说明这些概念:
- 局部变量:定义在方法内部的变量,在方法执行时创建,在方法执行结束后销毁。
public class Example {
public void method() {
int x = 10; // x是一个局部变量,只在method方法中可见
System.out.println(x);
}
}
- 类变量(静态变量):使用
static
关键字修饰的变量,属于类而不是特定的实例,所有实例共享同一个类变量的值。
public class Example {
public static int count = 0; // count是一个类变量,所有实例共享同一个count的值
public Example() {
count++; // 每次创建实例时,count增加1
}
}
- 成员变量(非静态变量):定义在类中但在方法之外的变量,每个实例都有自己的一组成员变量的副本。
public class Example {
private int x; // x是一个成员变量,每个实例都有自己的x值
public void setX(int x) {
this.x = x; // 设置实例的x值
}
public int getX() {
return x; // 获取实例的x值
}
}
这些示例展示了局部变量、类变量和成员变量的特点和用法,希望能帮助你更好地理解它们之间的区别。
内存存储方式
在底层内存存储上,这三种类型的变量确实有一些区别:
-
局部变量:局部变量通常存储在栈内存中。栈内存用于存储局部变量和方法调用的信息。当方法被调用时,栈帧被创建,包括局部变量的内存空间。当方法执行完成时,栈帧被销毁,局部变量的内存空间也随之释放。
-
类变量(静态变量):类变量存储在方法区(JVM规范中称为永久代或元空间)中。方法区用于存储类的结构信息、常量、静态变量等。类加载时,静态变量被分配在方法区中,它们在整个程序的生命周期内都存在,并且所有实例共享同一个静态变量的值。
-
成员变量(非静态变量):成员变量存储在堆内存中。堆内存用于存储对象实例及其成员变量。每个对象实例都有自己的一份成员变量的副本,存储在堆内存中。当对象实例被销毁时,其成员变量的内存空间也会被释放。
因此,这三种类型的变量在底层内存存储上有一些区别,主要是存储位置和生命周期的不同。
永久代(Permanent Generation)和元空间(Metaspace)1
在Java虚拟机中,永久代(Permanent Generation)和元空间(Metaspace)是两个不同的概念,它们分别用于存储类的结构信息、常量池、静态变量等。这两个概念存在于不同版本的Java虚拟机中,而且在Java 8及以后的版本中,永久代已经被元空间所取代。
具体来说:
-
永久代(Permanent Generation):永久代是Java虚拟机内存模型中的一个区域,主要用于存储类的结构信息、常量池、静态变量等。在Java 7及以前的版本中,永久代的大小是有限的,并且容易出现内存溢出的情况。因此,为了解决永久代内存溢出的问题,在Java 8中被元空间所取代。
-
元空间(Metaspace):元空间是Java虚拟机内存模型中的一个区域,用于存储类的结构信息、常量池、静态变量等。与永久代不同的是,元空间不再使用JVM堆内存,而是直接使用本地内存。这样可以避**免永久代内存溢出的问题,并且具有更高的灵活性和性能。****
**
总之,永久代和元空间都是用于存储类的结构信息等内容的内存区域,但它们存在于不同的Java虚拟机版本中,并且在内部实现和使用上有一些不同。
堆内存(Heap Memory) 栈内存(Stack Memory)和本地内存(Native Memory)
理解内存管理的概念确实很重要。让我详细介绍一下堆内存、栈内存和本地内存的区别:
-
堆内存(Heap Memory):
- 堆内存是Java虚拟机用于存储对象实例的内存区域。
- 所有通过
new
关键字创建的对象都存储在堆内存中。 - 堆内存由Java虚拟机动态分配和管理,它的大小可以通过启动参数来指定,也可以根据需要动态调整。
- 堆内存的特点是具有自动内存管理机制(垃圾回收),用于管理对象的创建、使用和销毁。
-
栈内存(Stack Memory):
- 栈内存是用于存储方法调用和局部变量的内存区域。
- 每个线程都有自己的栈内存,用于存储方法的调用栈和局部变量。
- 方法调用时,会在栈内存中创建一个栈帧(Stack Frame),用于存储方法的参数、局部变量和方法返回值等信息。
- 栈内存是由Java虚拟机动态分配和释放的,方法的调用结束后,相应的栈帧会被销毁。
-
本地内存(Native Memory):
- 本地内存是指操作系统提供的一块内存区域,不受Java虚拟机的管理,而是由操作系统直接分配和管理。
- 本地内存通常用于存储本地方法栈、本地方法库、JNI(Java Native Interface)方法等内容。
- 本地内存的特点是不受Java虚拟机的控制,可以直接通过操作系统提供的API来进行内存操作,但也因此需要开发人员自行负责内存的管理和释放。
简而言之,堆内存是Java虚拟机用于存储对象实例的内存区域,栈内存是用于存储方法调用和局部变量的内存区域,而本地内存是操作系统提供的一块内存区域,用于存储本地方法栈、本地方法库等内容。三者在使用和管理上有着不同的特点和机制。
永久代(Permanent Generation)和元空间(Metaspace)2
永久代(Permanent Generation)是Java虚拟机内存模型中的一个特殊区域,它并不是Java堆内存的一部分,而是一个独立的内存空间,专门用于存储类的结构信息、常量池、静态变量等内容。永久代的创建旨在优化Java虚拟机中的内存管理和类加载机制,提高内存管理的效率和性能。
在Java 7及以前的版本中,永久代是用来存储这些内容的主要内存区域。然而,在Java 8及以后的版本中,由于永久代存在一些问题,特别是容易导致内存溢出的情况,因此被元空间所取代。
元空间(Metaspace)是Java虚拟机内存模型中的另一个特殊区域,用于存储类的结构信息、常量池、静态变量等内容。与永久代不同的是,元空间不再使用Java堆内存,而是直接使用本地内存(Native Memory)。这样可以避免永久代内存溢出的问题,并且具有更高的灵活性和性能。
总之,永久代和元空间都是用于存储类的结构信息等内容的内存区域,但它们存在于不同的Java虚拟机版本中,并且在内部实现和使用上有一些不同。元空间在Java 8及以后的版本中取代了永久代,并且具有更高的灵活性和性能。
JVM 内存空间介绍
1. 程序计数器(Program Counter)
- 概念:
- 程序计数器是当前线程执行的字节码指令地址的指示器。
- 每个线程都有自己的程序计数器。
- 功能:
- 确定线程执行的位置,指示下一条要执行的指令。
- 在多线程环境下,确保线程切换后能够正确恢复执行位置。
- 存储方式:
- 程序计数器通常以寄存器的形式存在于CPU中,用于记录当前线程执行的位置。
- 举例:
- 在执行Java方法时,程序计数器会指示当前执行的方法字节码指令地址。
2. Java虚拟机栈(Java Virtual Machine Stack)
- 概念:
- Java虚拟机栈用于存储方法的调用和局部变量等信息。
- 每个线程都有自己的Java虚拟机栈。
- 功能:
- 管理方法调用的内存和执行过程中的局部变量。
- 保证方法调用的安全性和正确性。
- 存储方式:
- 每个方法调用在虚拟机栈中创建一个栈帧,用于存储方法的局部变量、操作数栈、动态链接、方法出口等信息。
- 举例:
- 在执行Java方法时,会根据方法调用情况在虚拟机栈中创建相应的栈帧来存储方法信息。
3. 堆(Heap)
- 概念:
- 堆是Java虚拟机中用于存储对象实例和数组等动态分配数据的内存区域。
- 是所有线程共享的内存区域。
- 功能:
- 提供内存给Java对象实例和数组使用。
- 动态分配内存,允许对象的动态创建和销毁。
- 存储方式:
- 由垃圾收集器管理的动态分配的内存块。
- 举例:
- 存储new关键字创建的对象和数组。
4. 方法区(Method Area)
- 概念:
- 方法区用于存储类的元数据、常量池、静态变量等信息。
- 是所有线程共享的内存区域。
- 功能:
- 存储类相关的元信息和静态变量。
- 用于运行时常量池的实现。
- 存储方式:
- 类加载时分配,可通过反射访问。
- 举例:
- 存储类的信息、静态变量和常量池中的常量。
5. 运行时常量池(Runtime Constant Pool)
- 概念:
- 每个类都有自己的运行时常量池,用于存储常量和符号引用。
- 是方法区的一部分。
- 功能:
- 存储类中的常量值和符号引用。
- 提供常量池的动态存储和访问。
- 存储方式:
- 类加载时动态生成,可通过反射访问。
- 举例:
- 存储字符串常量、类和方法的符号引用。
6. 本地方法栈(Native Method Stack)
- 概念:
- 用于存储本地方法(Native Method)的调用信息和数据。
- 类似于Java虚拟机栈,但用于本地方法的调用。
- 功能:
- 执行本地方法。
- 存储方式:
- 栈帧形式保存方法调用的信息。
- 举例:
- 调用C或C++编写的本地方法。
7. 直接内存(Direct Memory)
- 概念:
- 由NIO库提供的一种内存分配方式,允许Java程序直接使用操作系统的内存。
- 不受Java堆内存的限制。
- 功能:
- 提高I/O操作的性能。
- 存储方式:
- 直接使用操作系统的内存,通过ByteBuffer来操作。
- 举例:
- NIO中的Channel使用直接内存来提高I/O性能。
7. Java 数组
数组基本介绍
数组是储存在堆上的对象,可以保存多个同类型变量。在后面的章节中,我们将会学到如何声明、构造以及初始化一个数组。
数组的特点
在Java中,数组是一种特殊的数据结构,用于存储多个相同类型的元素。与其他对象一样,数组也是储存在堆上的对象。以下是有关Java数组的一些重要特点:
-
存储多个同类型变量:数组可以保存多个相同类型的元素,这些元素可以是基本数据类型(如int、double等)或对象类型(如String、自定义类等)
Ps: 是的,Java数组是同一种类型的元素的有序集合,因此只能存储相同类型的变量。换句话说,数组中的元素必须是同一种数据类型,例如整数、浮点数、字符串等。试图将不同类型的变量存储在同一个数组中会导致编译错误。这种严格的类型要求是Java语言的特性之一,有助于提高代码的可靠性和安全性。 -
声明数组:在Java中,声明数组需要指定数组的类型和名称,例如:
int[] myArray;
声明了一个整型数组变量myArray
。 -
构造数组:在声明数组后,需要通过new关键字来分配内存空间并创建数组对象,例如:
myArray = new int[5];
创建了一个包含5个整数元素的整型数组对象。 -
初始化数组:数组可以在声明时或者之后进行初始化。在声明时初始化数组,可以直接指定初始值,如:
int[]例 myArray = {1, 2, 3, 4, 5};
。在之后进行初始化,则需要通过索引逐个为数组元素赋值。
总的来说,Java数组是一种便捷的数据结构,用于存储多个同类型的元素。通过声明、构造和初始化数组,我们可以方便地操作和处理多个数据。
数据类型的赋值方式小总结
好的,让我为您介绍Java中常见的数据类型的赋值方式:
-
基本数据类型:
- 直接赋值:直接将数值赋给变量。
int num = 10; // 直接赋值
- 初始化列表赋值:在声明变量时,使用等号和数值进行赋值。
int num = new int(10); // 初始化列表赋值
- 用户输入赋值:通过用户输入的方式给变量赋值。
Scanner scanner = new Scanner(System.in); int num = scanner.nextInt(); // 用户输入赋值
- 计算赋值:通过计算得到的结果给变量赋值。
int a = 5; int b = 10; int result = a + b; // 计算赋值
- 直接赋值:直接将数值赋给变量。
-
引用数据类型(对象类型):
- 直接赋值:直接将对象赋给引用变量。
String str = "Hello"; // 直接赋值
- 初始化列表赋值:在声明引用变量时,使用等号和对象进行赋值。
String str = new String("Hello"); // 初始化列表赋值
- 用户输入赋值:通过用户输入的方式给引用变量赋值。
Scanner scanner = new Scanner(System.in); String str = scanner.nextLine(); // 用户输入赋值
- 方法返回值赋值:通过方法返回的结果给引用变量赋值。
String str = getHello(); // 方法返回值赋值
- 直接赋值:直接将对象赋给引用变量。
-
数组类型:
- 直接赋值:直接在声明数组时初始化数组元素。
int[] arr = {1, 2, 3}; // 直接赋值
- 初始化列表赋值:在声明数组时使用大括号括起来的列表进行赋值。
int[] arr = new int[]{1, 2, 3}; // 初始化列表赋值
- 索引赋值:通过循环结构逐个访问数组中的元素,并为每个元素赋予特定的值。
int[] arr = new int[5]; for (int i = 0; i < arr.length; i++) { arr[i] = i * 2; // 索引赋值 }
- 直接赋值:直接在声明数组时初始化数组元素。
这些是Java中常见的数据类型的赋值方式,根据具体的场景和需求选择最适合的方式来进行赋值操作。
更多的赋值方式:
-
枚举类型(Enum):
- 直接赋值:直接将枚举常量赋给枚举变量。
enum Day { MONDAY, TUESDAY, WEDNESDAY } Day today = Day.MONDAY; // 直接赋值
- 枚举索引赋值:通过枚举常量的索引值给枚举变量赋值。
Day today = Day.values()[0]; // 枚举索引赋值
- 枚举方法赋值:通过调用枚举常量的方法返回结果给枚举变量赋值。
Day today = Day.valueOf("MONDAY"); // 枚举方法赋值
-
泛型类型(Generic):
- 直接赋值:直接使用泛型对象进行赋值。
List<String> list = new ArrayList<>(); // 直接赋值
- 泛型方法赋值:通过调用泛型方法返回结果给泛型变量赋值。
List<String> list = Collections.emptyList(); // 泛型方法赋值
- 泛型通配符赋值:通过使用通配符的方式赋值。
List<?> list = new ArrayList<>(); // 泛型通配符赋值
-
自定义类(Class):
- 初始化列表赋值:在声明对象变量时,使用等号和new关键字创建对象进行赋值。
MyClass obj = new MyClass(); // 初始化列表赋值
- 构造方法赋值:通过调用对象的构造方法来创建对象并赋值。
MyClass obj = new MyClass("parameter"); // 构造方法赋值
- 对象方法赋值:通过调用对象的方法返回结果给对象变量赋值。
MyClass obj = getMyClass(); // 对象方法赋值
数据类型分类
数据类型可以分为两大类:基本数据类型(Primitive Data Types)和引用数据类型(Reference Data Types)。
-
基本数据类型:
- 整型(Integral Types):用于表示整数,包括byte、short、int、long四种类型。
- byte:1字节,范围为-128到127。
- short:2字节,范围为-32,768到32,767。
- int:4字节,范围为-231到231-1。
- long:8字节,范围为-263到263-1。
- 浮点型(Floating-Point Types):用于表示带小数部分的数字,包括float和double两种类型。
- float:4字节,范围为IEEE 754格式的单精度浮点数。
- double:8字节,范围为IEEE 754格式的双精度浮点数。
- 字符型(Character Type):用于表示单个字符,char类型,占2字节。
- 布尔型(Boolean Type):用于表示逻辑值,boolean类型,只有两个值:true和false。
- 整型(Integral Types):用于表示整数,包括byte、short、int、long四种类型。
-
引用数据类型:
- 类型(Class Type):用于表示自定义的对象,包括类、接口、数组等。
- 接口类型(Interface Type):用于表示接口。
- 数组类型(Array Type):用于表示数组对象。
基本数据类型是存储基本数值的类型,而引用数据类型是存储对象引用的类型。在Java中,基本数据类型的变量直接存储数值,而引用数据类型的变量存储的是对象的引用,实际的对象数据存储在堆内存中。
引用数据类型详解
当我们谈论引用数据类型时,我们实际上是在谈论对象的引用,而不是对象本身。引用数据类型在Java中用于表示对象的引用或地址,而不是对象本身的实际数据。
-
类类型(Class Type):
- 类是一种用户自定义的数据类型,它可以包含属性(字段)和方法。
- 通过类可以创建多个对象,每个对象都有自己的属性和方法。
- 类型本身并不存储数据,而是定义了对象的结构和行为。
- 例如:
class MyClass { int x; void myMethod() { System.out.println("Hello from MyClass"); } }
-
接口类型(Interface Type):
- 接口是一种抽象的数据类型,它定义了一组方法的签名,但没有方法的具体实现。
- 类可以实现一个或多个接口,从而获得接口定义的方法。
- 接口用于实现多态性和规范类的行为。
- 例如:
interface MyInterface { void myMethod(); }
-
数组类型(Array Type):
- 数组是一种特殊的引用数据类型,它用于存储多个相同类型的元素。
- 数组可以是基本数据类型的数组,也可以是引用数据类型的数组。
- 数组的长度是固定的,一旦创建后无法改变。
- 例如:
int[] numbers = new int[5]; String[] names = new String[3];
引用数据类型的变量存储的是对象的地址或引用,而不是对象本身的数据。当我们创建一个对象时,实际上是在堆内存中分配了内存空间,并返回了对象的引用,然后将这个引用赋给引用数据类型的变量。这样,变量就可以通过引用访问对象的属性和方法。
8. Java 枚举
Java 5.0引入了枚举,枚举限制变量只能是预先设定好的值。使用枚举可以减少代码中的 bug。
例如,我们为果汁店设计一个程序,它将限制果汁为小杯、中杯、大杯。这就意味着它不允许顾客点除了这三种尺寸外的果汁。
实例
class FreshJuice {
// 定义一个枚举类型表示果汁的大小
enum FreshJuiceSize {
SMALL, // 小杯
MEDIUM, // 中杯
LARGE // 大杯
}
// 声明一个枚举类型的变量表示果汁的大小
FreshJuiceSize size;
}
public class FreshJuiceTest {
public static void main(String[] args) {
// 创建一个FreshJuice对象
FreshJuice juice = new FreshJuice();
// 为对象的size属性赋值为MEDIUM,表示中杯大小
juice.size = FreshJuice.FreshJuiceSize.MEDIUM;
}
}
**注意:**枚举可以单独声明或者声明在类里面。方法、变量、构造函数也可以在枚举中定义。
如何修改枚举类型的值
枚举类型中的值通常是在定义时确定的,并且在程序运行时是不可更改的。但是,如果你需要修改枚举类型中的值,你可以按照以下步骤来实现:
- 修改枚举类型的定义:你可以直接修改枚举类型的定义,更改已有枚举常量的值或者添加新的枚举常量。
enum Weekday {
MONDAY, // 星期一
TUESDAY, // 星期二
WEDNESDAY, // 星期三
THURSDAY, // 星期四
FRIDAY // 星期五
}
- 编译并重新运行程序:在修改了枚举类型的定义后,需要重新编译并运行程序,以使修改生效。
public class Main {
public static void main(String[] args) {
Weekday today = Weekday.WEDNESDAY;
System.out.println("Today is " + today);
}
}
在这个例子中,如果你希望将 WEDNESDAY 修改为 WEEKEND,你可以将枚举类型的定义修改为:
enum Weekday {
MONDAY, // 星期一
TUESDAY, // 星期二
WEEKEND, // 周末
THURSDAY, // 星期四
FRIDAY // 星期五
}
然后重新编译并运行程序,今天的值将会被更新为 WEEKEND。
9. Java 关键字
下面列出了 Java 关键字。这些保留字不能用于常量、变量、和任何标识符的名称。
类别 | 关键字 | 说明 |
---|---|---|
访问控制 | private | 私有的 |
protected | 受保护的 | |
public | 公共的 | |
default | 默认 | |
类、方法和变量修饰符 | abstract | 声明抽象 |
class | 类 | |
extends | 扩充、继承 | |
final | 最终值、不可改变的 | |
implements | 实现(接口) | |
interface | 接口 | |
native | 本地、原生方法(非 Java 实现) | |
new | 创建 | |
static | 静态 | |
strictfp | 严格浮点、精准浮点 | |
synchronized | 线程、同步 | |
transient | 短暂 | |
volatile | 易失 | |
程序控制语句 | break | 跳出循环 |
case | 定义一个值以供 switch 选择 | |
continue | 继续 | |
do | 运行 | |
else | 否则 | |
for | 循环 | |
if | 如果 | |
instanceof | 实例 | |
return | 返回 | |
switch | 根据值选择执行 | |
while | 循环 | |
错误处理 | assert | 断言表达式是否为真 |
catch | 捕捉异常 | |
finally | 有没有异常都执行 | |
throw | 抛出一个异常对象 | |
throws | 声明一个异常可能被抛出 | |
try | 捕获异常 | |
包相关 | import | 引入 |
package | 包 | |
基本类型 | boolean | 布尔型 |
byte | 字节型 | |
char | 字符型 | |
double | 双精度浮点 | |
float | 单精度浮点 | |
int | 整型 | |
long | 长整型 | |
short | 短整型 | |
变量引用 | super | 父类、超类 |
this | 本类 | |
void | 无返回值 | |
保留关键字 | goto | 是关键字,但不能使用 |
const | 是关键字,但不能使用 | |
**注意:**Java 的 null 不是关键字,类似于 true 和 false,它是一个字面常量,不允许作为标识符使用。 |
10. Java注释
类似于 C/C++、Java 也支持单行以及多行注释。
注释中的字符将被 Java 编译器忽略。
public class HelloWorld {
/* 这是第一个Java程序
* 它将输出 Hello World
* 这是一个多行注释的示例
*/
public static void main(String[] args){
// 这是单行注释的示例
/* 这个也是单行注释的示例 */
System.out.println("Hello World");
}
}
更多内容可以参考:Java 注释。
Java 注释总结
Java 支持多种注释方式,主要包括单行注释、多行注释和文档注释。
-
单行注释: 使用
//
来表示,从//
开始到行末都被注释掉。// 这是单行注释 int x = 10; // 这是另一个单行注释
-
多行注释: 使用
/*
开头和*/
结尾,中间的内容都被注释掉。/* * 这是多行注释 * 可以跨越多行 */ int y = 20; /* 这是另一个多行注释 */
-
文档注释: 用于生成代码文档,通常在类、方法、字段的前面使用
/** ... */
形式的注释。/** * 这是文档注释 * 用于生成代码文档 */ public class MyClass { /** * 这是一个方法 * @param x 参数 x * @return 返回值 */ public int myMethod(int x) { return x * x; } }
文档注释通常包含对代码的详细描述,以及使用@param
和@return
等标签来指定方法的参数和返回值的含义。
11. Java 空行
空白行或者有注释的行,Java 编译器都会忽略掉。
12. 继承
在 Java 中,一个类可以由其他类派生。如果你要创建一个类,而且已经存在一个类具有你所需要的属性或方法,那么你可以将新创建的类继承该类。
利用继承的方法,可以重用已存在类的方法和属性,而不用重写这些代码。被继承的类称为超类(super class),派生类称为子类(sub class)。
理解继承的全部概念需要掌握以下内容:
-
父类和子类:
- 父类是被继承的类,子类是继承父类的类。
- 子类**继承了父类的属性和方法,并且可以添加新的属性和方法。****
-
extends关键字:
- 使用
extends
关键字声明一个类继承另一个类。 - 子类可以继承父类的非私有属性和方法。
- 使用
-
单继承:
- Java不支持多继承,一个类只能继承一个直接父类。
-
重写(Override):
- 子类可以重写父类的方法,即在子类中重新实现父类的方法,以满足子类的特定需求。
- 重写的方法具有相同的名称、参数列表和返回类型。
-
super关键字:
- 子类可以使用
super
关键字调用父类的方法或访问父类的属性。 - 使用
super()
调用父类的构造方法。
- 子类可以使用
-
构造方法:
- 子类的构造方法可以调用父类的构造方法来初始化继承的属性。
- 如果子类没有显式地调用父类的构造方法,则会隐式地调用父类的默认构造方法。
-
继承链:
- 继承可以形成一个继承链,一个类可以作为多个子类的父类,也可以是其他类的子类。
-
抽象类和接口:
- Java中的抽象类和接口提供了更高级别的继承机制,允许定义没有实现的方法,从而使得子类必须实现这些方法。
理解这些概念可以帮助你全面掌握Java中的继承机制,进而更好地设计和编写面向对象的程序。
实例
好的,让我通过一个例子来说明Java的继承机制。
假设我们有一个Vehicle
类,表示各种交通工具,其中包括属性brand
(品牌)和方法run()
(行驶):
class Vehicle {
String brand;
public Vehicle(String brand) {
this.brand = brand;
}
public void run() {
System.out.println(brand + " is running.");
}
}
现在,我们要创建一个子类Car
来表示汽车,汽车除了继承父类的属性和方法外,还有自己独特的属性numOfSeats
(座位数):
class Car extends Vehicle {
int numOfSeats;
public Car(String brand, int numOfSeats) {
super(brand); // 调用父类的构造方法来初始化brand属性
this.numOfSeats = numOfSeats;
}
// 方法重写,重写父类的run方法
@Override
public void run() {
System.out.println(brand + " car with " + numOfSeats + " seats is running.");
}
// Car类的特有方法
public void honk() {
System.out.println("Honk honk!");
}
}
现在,我们来使用这些类:
public class Main {
public static void main(String[] args) {
// 创建一个汽车对象
Car myCar = new Car("Toyota", 5);
// 调用继承自父类的方法
myCar.run(); // 输出:Toyota car with 5 seats is running.
// 调用子类自己的方法
myCar.honk(); // 输出:Honk honk!
}
}
在这个例子中,Car
类继承了Vehicle
类的属性brand
和方法run()
,并添加了自己的属性numOfSeats
和方法honk()
。通过调用父类的构造方法super(brand)
来初始化父类的属性,通过@Override
注解重写父类的run()
方法,以实现子类特有的功能。
13. 接口
在 Java 中,接口可理解为对象间相互通信的协议。接口在继承中扮演着很重要的角色。
接口只定义派生要用到的方法,但是方法的具体实现完全取决于派生类。
接口是Java编程中的一种重要概念,它定义了一组抽象方法的集合,但没有具体的实现。下面是关于接口的一些详细信息:
-
定义: 接口使用
interface
关键字定义,其中包含一组抽象方法的声明,这些方法通常没有具体的实现。 -
抽象方法: 接口中的方法通常是抽象的,不包含方法体。这些方法只有方法签名,没有具体的实现逻辑。
-
特性:
- 接口中的方法默认为
public abstract
,可以省略这些修饰符。 - 接口中可以包含常量(
public static final
修饰的变量),这些常量默认为public static final
。 - 接口不能包含实例变量。
- 一个类可以实现多个接口。
- 接口中的方法默认为
-
实现接口: 类实现接口时,需要使用
implements
关键字,并提供接口中所有抽象方法的具体实现。 -
多态性: 接口的一个重要作用是实现多态性。通过接口,可以将不同类的对象视为同一类型,从而实现针对接口编程。
-
扩展性: 接口提供了一种灵活的方式来设计和扩展类的行为,通过实现不同的接口,可以实现不同的功能组合。
-
使用场景:
- 接口用于定义类之间的契约关系,实现了相同接口的类必须提供相同的行为。
- 接口用于实现回调函数,允许对象在某些事件发生时通知其他对象。
- 接口用于实现多继承,因为Java类不支持多继承,但是一个类可以实现多个接口。
总的来说,接口提供了一种规范和契约,用于定义类之间的通信和行为,从而增强了代码的灵活性、可维护性和可扩展性。
14. Java 源程序与编译型运行区别
如下图所示:
下一节介绍 Java 编程中的类和对象。之后你将会对 Java 中的类和对象有更清楚的认识。