Java教程
(声明:本文来自菜鸟教程加个人理解,适用于有一定语言基础的小伙伴食用)
Java简介★
主要特性
-
Java 语言是简单的:
Java 语言的语法与 C 语言和 C++ 语言很接近,使得大多数程序员很容易学习和使用。另一方面,Java 丢弃了 C++ 中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换。特别地,Java 语言不使用指针,而是引用。并提供了自动分配和回收内存空间,使得程序员不必为内存管理而担忧。
-
Java 语言是面向对象的:
Java 语言提供类、接口和继承等面向对象的特性,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为 implements)。Java 语言全面支持动态绑定,而 C++语言只对虚函数使用动态绑定。总之,Java语言是一个纯的面向对象程序设计语言。
-
Java语言是分布式的:
Java 语言支持 Internet 应用的开发,在基本的 Java 应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括 URL、URLConnection、Socket、ServerSocket 等。Java 的 RMI(远程方法激活)机制也是开发分布式应用的重要手段。
-
Java 语言是健壮的:
Java 的强类型机制、异常处理、垃圾的自动收集等是 Java 程序健壮性的重要保证。对指针的丢弃是 Java 的明智选择。Java 的安全检查机制使得 Java 更具健壮性。
-
Java语言是安全的:
Java通常被用在网络环境中,为此,Java 提供了一个安全机制以防恶意代码的攻击。除了Java 语言具有的许多安全特性以外,Java 对通过网络下载的类具有一个安全防范机制(类 ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查,并提供安全管理机制(类 SecurityManager)让 Java 应用设置安全哨兵。
-
Java 语言是体系结构中立的:
Java 程序(后缀为 java 的文件)在 Java 平台上被编译为体系结构中立的字节码格式(后缀为 class 的文件),然后可以在实现这个 Java 平台的任何系统中运行。这种途径适合于异构的网络环境和软件的分发。
-
Java 语言是可移植的:
这种可移植性来源于体系结构中立性,另外,Java 还严格规定了各个基本数据类型的长度。Java 系统本身也具有很强的可移植性,Java 编译器是用 Java 实现的,Java 的运行环境是用 ANSI C 实现的。
-
Java 语言是解释型的:
如前所述,Java 程序在 Java 平台上被编译为字节码格式,然后可以在实现这个 Java 平台的任何系统中运行。在运行时,Java 平台中的 Java 解释器对这些字节码进行解释执行,执行过程中需要的类在联接阶段被载入到运行环境中。
-
Java 是高性能的:
与那些解释型的高级脚本语言相比,Java 的确是高性能的。事实上,Java 的运行速度随着 JIT(Just-In-Time)编译器技术的发展越来越接近于 C++。
-
Java 语言是多线程的:
在 Java 语言中,线程是一种特殊的对象,它必须由 Thread 类或其子(孙)类来创建。通常有两种方法来创建线程:其一,使用型构为 Thread(Runnable) 的构造子类将一个实现了 Runnable 接口的对象包装成一个线程,其二,从 Thread 类派生出子类并重写 run 方法,使用该子类创建的对象即为线程。值得注意的是 Thread 类已经实现了 Runnable 接口,因此,任何一个线程均有它的 run 方法,而 run 方法中包含了线程所要运行的代码。线程的活动由一组方法来控制。Java 语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为 synchronized)。
-
Java 语言是动态的:
Java 语言的设计目标之一是适应于动态变化的环境。Java 程序需要的类能够动态地被载入到运行环境,也可以通过网络来载入所需要的类。这也有利于软件的升级。另外,Java 中的类有一个运行时刻的表示,能进行运行时刻的类型检查。
Java 开发环境配置
Java AI 编程助手
Java 基础语法
一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。
- 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
- 类:类是一个模板,它描述一类对象的行为和状态。
- 方法:方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
- 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
基本语法
编写 Java 程序时,应注意以下几点:
- 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的 Java 程序由 public static void main(String[] args) 方法开始执行
java标识符
- 合法标识符举例:age、$salary、_value、__1_value
- 非法标识符举例:123abc、-salary
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,它是一个字面常量,不允许作为标识符使用。
Java 空行
空白行或者有注释的行,Java 编译器都会忽略掉。
Java 源程序与编译型运行区别
如下图所示:
“半编译半解释”语言
Java 既不是纯粹的解释型语言,也不是纯粹的编译型语言,而是两者结合的一种语言,通常被称为“半编译半解释”语言。这种特性是由 Java 的执行机制决定的:
编译过程:
- 当你编写完 Java 代码后,首先需要通过 Java 编译器(
javac
)将源代码(.java
文件)编译成字节码(.class
文件)。这个过程与传统编译型语言类似,生成的字节码是一种中间形式的代码,独立于具体的机器架构。
解释过程:
- 字节码文件随后会被加载到 Java 虚拟机(JVM)中运行。JVM 是一个虚拟的计算机,它负责解释并执行字节码。JVM 会根据运行时的具体环境(如操作系统和硬件),将字节码转换成对应的机器码来执行。
即时编译(Just-In-Time Compilation, JIT):
- 为了提高性能,现代的 JVM 实现了即时编译技术。JIT 编译器会在运行时分析程序的执行情况,将频繁执行的字节码部分直接编译成机器码,从而避免每次执行时都需要进行解释。这样可以显著提高程序的执行效率。
总结来说,Java 代码首先被编译成字节码,然后在 JVM 中通过解释或即时编译的方式执行。这种方式结合了编译型语言的性能优势和解释型语言的灵活性,使得 Java 具有“一次编写,到处运行”的特点。
Java注释
单行注释:
和C语言一样,单行注释以双斜杠 // 开始:
多行注释:
和C语言一样,多行注释以 /*开始,以 */结束:
文档注释:
文档注释的格式通常包含一些特定的标签,如 @param 用于描述方法参数,@return 用于描述返回值,@throws 用于描述可能抛出的异常等等,这些标签有助于生成清晰的API文档,以便其他开发者能够更好地理解和使用你的代码。
Java文档注释:
Java 文档注释(也称为 Javadoc 注释)是 Java 特有的一种注释方式,用于生成 API 文档。Javadoc 是 Sun Microsystems(现在是 Oracle)提供的一种工具,它可以解析源代码中的特殊注释,生成 HTML 格式的文档。这些文档通常包括类、接口、方法和字段的详细说明,帮助开发者更好地理解和使用代码。更多文档注释的内容可以参考:Java 文档注释。
实例
下面是一个使用说明注释的简单实例。注意每一个注释都在它描述的项目的前面。
在经过 javadoc 处理之后,SquareNum 类的注释将在 SquareNum.html 中找到。
import java.io.*;
/**
* 这个类演示了文档注释
* @author Ayan Amhed
* @version 1.2
*/
public class SquareNum {
/**
* This method returns the square of num.
* This is a multiline description. You can use
* as many lines as you like.
* @param num The value to be squared.
* @return num squared.
*/
public double square(double num) {
return num * num;
}
/**
* This method inputs a number from the user.
* @return The value input as a double.
* @exception IOException On input error.
* @see IOException
*/
public double getNumber() throws IOException {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader inData = new BufferedReader(isr);
String str;
str = inData.readLine();
return (new Double(str)).doubleValue();
}
/**
* This method demonstrates square().
* @param args Unused.
* @return Nothing.
* @exception IOException On input error.
* @see IOException
*/
public static void main(String args[]) throws IOException
{
SquareNum ob = new SquareNum();
double val;
System.out.println("Enter value to be squared: ");
val = ob.getNumber();
val = ob.square(val);
System.out.println("Squared value is " + val);
}
}
Java对象和类★★★★★★
Java 作为一种面向对象的编程语言,支持以下基本概念:
1、类(Class):
- 定义对象的蓝图,包括属性和方法。
-
示例:
public class Car { ... }
2、对象(Object):
- 类的实例,具有状态和行为。
- 示例:
Car myCar = new Car();
3、继承(Inheritance):
- 一个类可以继承另一个类的属性和方法。
- 示例:
public class Dog extends Animal { ... }
4、封装(Encapsulation):
- 将对象的状态(字段)私有化,通过公共方法访问。
- 示例:
private String name; public String getName() { return name; }
5、多态(Polymorphism):
- 对象可以表现为多种形态,主要通过方法重载和方法重写实现。
- 示例:
- 方法重载:
public int add(int a, int b) { ... }
和public double add(double a, double b) { ... }
- 方法重写:
@Override public void makeSound() { System.out.println("Meow"); }
- 方法重载:
6、抽象(Abstraction):
- 使用抽象类和接口来定义必须实现的方法,不提供具体实现。
- 示例:
- 抽象类:
public abstract class Shape { abstract void draw(); }
- 接口:
public interface Animal { void eat(); }
- 抽象类:
7、接口(Interface):
- 定义类必须实现的方法,支持多重继承。
- 示例:
public interface Drivable { void drive(); }
8、方法(Method):
- 定义类的行为,包含在类中的函数。
- 示例:
public void displayInfo() { System.out.println("Info"); }
9、方法重载(Method Overloading):
- 同一个类中可以有多个同名的方法,但参数不同。
- 示例:
public class MathUtils {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
问:Java的public static void main是他的主类吗
答:在 Java 中,public static void main 方法并不是“主类”,而是程序的入口点。每个 Java 应用程序都必须有一个 main 方法,这是 JVM(Java 虚拟机)启动程序的地方。
main 方法的定义
public static void main(String[] args) {
// 程序的起点
}
- public:访问修饰符,表示该方法对所有类可见。
- static:静态修饰符,表示该方法属于类本身,而不是类的实例。因此,可以在没有创建类的实例的情况下调用该方法。
- void:返回类型,表示该方法不返回任何值。
- main:方法名,必须是 main,这是 JVM 规定的。
- String[] args:参数列表,通常用于接收命令行参数。
- 主类:包含 main 方法的类被称为主类。主类是程序的起点。
- 命名:主类的名称通常与包含它的文件名相同。例如,如果主类名为 MyApp,则文件名应为 MyApp.java。
一个Java 程序是指一个可以独立运行的单元,这个单元必须包含一个 main 方法作为程序的入口点。一个完整的 Java 程序是以 public static void main(String[] args) 方法为标志的。这个方法是 Java 虚拟机(JVM)启动程序的入口点。
构造方法
每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。下面是一个构造方法示例:
public class Puppy{
public Puppy(){
}
public Puppy(String name){
// 这个构造器仅有一个参数:name
}
}
创建对象
对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型。
- 实例化:使用关键字 new 来创建一个对象。
- 初始化:使用 new 创建对象时,会调用构造方法初始化对象。
下面是一个创建对象的例子:
public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}
实例
下面的例子展示如何访问实例变量和调用成员方法:
public class Puppy {
private int age;
private String name;
// 构造器
public Puppy(String name) {
this.name = name;
System.out.println("小狗的名字是 : " + name);
}
// 设置 age 的值
public void setAge(int age) {
this.age = age;
}
// 获取 age 的值
public int getAge() {
return age;
}
// 获取 name 的值
public String getName() {
return name;
}
// 主方法
public static void main(String[] args) {
// 创建对象
Puppy myPuppy = new Puppy("Tommy");
// 通过方法来设定 age
myPuppy.setAge(2);
// 调用另一个方法获取 age
int age = myPuppy.getAge();
System.out.println("小狗的年龄为 : " + age);
// 也可以直接访问成员变量(通过 getter 方法)
System.out.println("变量值 : " + myPuppy.getAge());
}
}
源文件声明规则
在本节的最后部分,我们将学习源文件的声明规则。当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。
- 一个源文件中只能有一个 public 类
- 一个源文件可以有多个非 public 类
- 源文件的名称应该和 public 类的类名保持一致。例如:源文件中 public 类的类名是 Employee,那么源文件应该命名为Employee.java。
- 如果一个类定义在某个包中,那么 package 语句应该在源文件的首行。
- 如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。如果没有 package 语句,那么 import 语句应该在源文件中最前面。
- import 语句和 package 语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
类有若干种访问级别,并且类也分不同的类型:抽象类和 final 类等。这些将在访问控制章节介绍。除了上面提到的几种类型,Java 还有一些特殊的类,如:内部类、匿名类。
Java 包
包主要用来对类和接口进行分类。当开发 Java 程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。
import 语句
在 Java 中,如果给出一个完整的限定名,包括包名、类名,那么 Java 编译器就可以很容易地定位到源代码或者类。import 语句就是用来提供一个合理的路径,使得编译器可以找到某个类。
例如,下面的命令行将会命令编译器载入 java_installation/java/io 路径下的所有类
import java.io.*;
一个简单的例子
(至于简单与否,看你的水平了)在该例子中,我们创建两个类:Employee 和 EmployeeTest。Employee 类有四个成员变量:name、age、designation 和 salary,该类显式声明了一个构造方法,该方法只有一个参数。
import java.io.*;
public class Employee {
private String name;
private int age;
private String designation;
private double salary;
// Employee 类的构造器
public Employee(String name) {
this.name = name;
}
// 设置 age 的值
public void setAge(int age) {
this.age = age;
}
// 获取 age 的值
public int getAge() {
return age;
}
// 设置 designation 的值
public void setDesignation(String designation) {
this.designation = designation;
}
// 获取 designation 的值
public String getDesignation() {
return designation;
}
// 设置 salary 的值
public void setSalary(double salary) {
this.salary = salary;
}
// 获取 salary 的值
public double getSalary() {
return salary;
}
// 打印信息
public void printEmployee() {
System.out.println(this);
}
// 重写 toString 方法
@Override
public String toString() {
return "名字: " + name + "\n" +
"年龄: " + age + "\n" +
"职位: " + designation + "\n" +
"薪水: " + salary;
}
}
Java 程序都是从 main 方法开始执行,为了能运行这个程序,必须包含 main 方法并且创建一个实例对象。下面给出 EmployeeTest 类,该类实例化 2 个 Employee 类的实例,并调用方法设置变量的值。将下面的代码保存在 EmployeeTest.java文件中。
import java.io.*;
public class EmployeeTest {
public static void main(String[] args) {
// 使用构造器创建两个对象
Employee empOne = new Employee("RUNOOB1");
Employee empTwo = new Employee("RUNOOB2");
// 调用这两个对象的成员方法
empOne.setAge(26);
empOne.setDesignation("高级程序员");
empOne.setSalary(1000);
empOne.printEmployee();
empTwo.setAge(21);
empTwo.setDesignation("菜鸟程序员");
empTwo.setSalary(500);
empTwo.printEmployee();
}
}
// 编译这两个文件并且运行 EmployeeTest 类,可以看到如下结果:
// $ javac EmployeeTest.java Employee.java
// $ java EmployeeTest
// 名字:RUNOOB1
// 年龄:26
// 职位:高级程序员
// 薪水:1000.0
// 名字:RUNOOB2
// 年龄:21
// 职位:菜鸟程序员
// 薪水:500.0
Java基本数据类型★★★
内置数据类型
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型)(byte、short、int、long、float、double),一种字符类型,还有一种布尔型。(char、boolean)
实例:
对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:
public class PrimitiveTypeTest {
public static void main(String[] args) {
// byte
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
System.out.println();
// short
System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
System.out.println("包装类:java.lang.Short");
System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
System.out.println();
// int
System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
System.out.println("包装类:java.lang.Integer");
System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
System.out.println();
// long
System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
System.out.println("包装类:java.lang.Long");
System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
System.out.println();
// float
System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
System.out.println("包装类:java.lang.Float");
System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
System.out.println();
// double
System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
System.out.println("包装类:java.lang.Double");
System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
System.out.println();
// char
System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
System.out.println("包装类:java.lang.Character");
// 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="
+ (int) Character.MIN_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最大值:Character.MAX_VALUE="
+ (int) Character.MAX_VALUE);
}
}
// 理应输出为:
// 基本类型:byte 二进制位数:8
// 包装类:java.lang.Byte
// 最小值:Byte.MIN_VALUE=-128
// 最大值:Byte.MAX_VALUE=127
// 基本类型:short 二进制位数:16
// 包装类:java.lang.Short
// 最小值:Short.MIN_VALUE=-32768
// 最大值:Short.MAX_VALUE=32767
// 基本类型:int 二进制位数:32
// 包装类:java.lang.Integer
// 最小值:Integer.MIN_VALUE=-2147483648
// 最大值:Integer.MAX_VALUE=2147483647
// 基本类型:long 二进制位数:64
// 包装类:java.lang.Long
// 最小值:Long.MIN_VALUE=-9223372036854775808
// 最大值:Long.MAX_VALUE=9223372036854775807
// 基本类型:float 二进制位数:32
// 包装类:java.lang.Float
// 最小值:Float.MIN_VALUE=1.4E-45
// 最大值:Float.MAX_VALUE=3.4028235E38
// 基本类型:double 二进制位数:64
// 包装类:java.lang.Double
// 最小值:Double.MIN_VALUE=4.9E-324
// 最大值:Double.MAX_VALUE=1.7976931348623157E308
// 基本类型:char 二进制位数:16
// 包装类:java.lang.Character
// 最小值:Character.MIN_VALUE=0
// 最大值:Character.MAX_VALUE=65535
引用类型
- 在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。
- 对象、数组都是引用数据类型。
- 所有引用类型的默认值都是null。
- 一个引用变量可以用来引用任何与之兼容的类型。
- 例子:Site site = new Site("Runoob")。
Java 常量
常量在程序运行时是不能被修改的。在 Java 中使用 final 关键字来修饰常量,声明方式和变量类似:(为了便于识别,通常使用大写字母表示常量。)
final double PI = 3.1415927;
类型转换
Java变量类型★★★★
Java 语言支持的变量类型有:
- 局部变量(Local Variables):局部变量是在方法、构造函数或块内部声明的变量,它们在声明的方法、构造函数或块执行结束后被销毁,局部变量在声明时需要初始化,否则会导致编译错误。
- 实例变量(Instance Variables):实例变量是在类中声明,但在方法、构造函数或块之外,它们属于类的实例,每个类的实例都有自己的副本,如果不明确初始化,实例变量会被赋予默认值(数值类型为0,boolean类型为false,对象引用类型为null)。(有点像C语言的全局变量,只是因为Java程序,是以类为单位的)
- 静态变量或类变量(Class Variables):类变量是在类中用 static 关键字声明的变量,它们属于类而不是实例,所有该类的实例共享同一个类变量的值,类变量在类加载时被初始化,而且只初始化一次。
- 参数变量(Parameters):参数是方法或构造函数声明中的变量,用于接收调用该方法或构造函数时传递的值,参数变量的作用域只限于方法内部。
成员变量和实例变量这两个术语在某些情况下被互换使用,主要是因为它们之间存在重叠,而且在很多实际应用中,成员变量通常指的是实例变量。不过,严格来说,它们是有区别的。成员变量包括实例变量 和 静态变量。实例:
public class RunoobTest {
// 成员变量
private int instanceVar;
// 静态变量
private static int staticVar;
public void method(int paramVar) {
// 局部变量
int localVar = 10;
// 使用变量
instanceVar = localVar;
staticVar = paramVar;
System.out.println("成员变量: " + instanceVar);
System.out.println("静态变量: " + staticVar);
System.out.println("参数变量: " + paramVar);
System.out.println("局部变量: " + localVar);
}
public static void main(String[] args) {
RunoobTest v = new RunoobTest();
v.method(20);
}
}
类变量…………
Java变量命名规则★
驼峰命名法
// 使用驼峰命名法
public static int myStaticVariable;
// 使用大写蛇形命名法
public static final int MAX_SIZE = 100;
Java修饰符★★★★★
访问修饰符(public、private、protected、default)
非访问修饰符(static、final、abstract、synchronized、transient、volatile)
访问控制修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
-
default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
-
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
-
public : 对所有类可见。使用对象:类、接口、变量、方法
-
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
我们可以通过以下表来说明访问权限:
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
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 |
默认访问修饰符-不使用任何关键字
如果在类、变量、方法或构造函数的定义中没有指定任何访问修饰符,那么它们就默认具有默认访问修饰符。默认访问修饰符的访问级别是包级别(package-level),即只能被同一包中的其他类访问。如下例所示,变量和方法的声明可以不使用任何修饰符。
// MyClass.java
class MyClass { // 默认访问修饰符
int x = 10; // 默认访问修饰符
void display() { // 默认访问修饰符
System.out.println("Value of x is: " + x);
}
}
// MyOtherClass.java
class MyOtherClass {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.display(); // 访问 MyClass 中的默认访问修饰符变量和方法
}
}
以上实例中,MyClass 类和它的成员变量 x 和方法 display() 都使用默认访问修饰符进行了定义。MyOtherClass 类在同一包中,因此可以访问 MyClass 类和它的成员变量和方法。
protected 是最难理解的一种 Java 类成员访问权限修饰词,更多详细内容请查看 Java protected 关键字详解。
访问控制和继承
请注意以下方法继承的规则:
-
父类中声明为 public 的方法在子类中也必须为 public。
-
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
-
父类中声明为 private 的方法,不能够被子类继承。
非访问修饰符
为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
- static 修饰符,用来修饰类方法和类变量。
- final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
- abstract 修饰符,用来创建抽象类和抽象方法。
- synchronized 和 volatile 修饰符,主要用于线程的编程。
static 修饰符
-
静态变量:
static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
-
静态方法:
static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
final 修饰符
final 变量:
final 表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。(之前提到的常量,就是使用final)
final 修饰符通常和 static 修饰符一起使用来创建类常量。
public class Test{
final int value = 10;
// 下面是声明常量的实例
public static final int BOXWIDTH = 6;
static final String TITLE = "Manager";
public void changeValue(){
value = 12; //将输出一个错误
}
}
final 方法
父类中的 final 方法可以被子类继承,但是不能被子类重写。声明 final 方法的主要目的是防止该方法的内容被修改。
如下所示,使用 final 修饰符声明方法。
public class Test{
public final void changeName(){
// 方法体
}
}
final 类
final 类不能被继承,没有类能够继承 final 类的任何特性。
public final class Test {
// 类体
}
abstract 修饰符
抽象类:
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。抽象类可以包含抽象方法和非抽象方法。
abstract class Caravan{
private double price;
private String model;
private String year;
public abstract void goFast(); //抽象方法
public abstract void changeColor(); // 抽象方法
}
抽象方法
抽象方法是一种没有任何实现的方法,该方法的具体实现由子类提供。
抽象方法不能被声明成 final 和 static。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。抽象方法的声明以分号结尾,例如:public abstract sample();。
public abstract class SuperClass{
abstract void m(); //抽象方法
}
class SubClass extends SuperClass{
//实现抽象方法
void m(){
.........
}
}
synchronized 修饰符
synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。
transient 修饰符
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
volatile 修饰符
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是 null。
Java运算符★★
Java位运算符★
位运算符
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A= 1100 0011
下表列出了位运算符的基本运算,假设整数变量 A 的值为 60 和变量 B 的值为 13:
操作符 | 描述 | 例子 |
---|---|---|
& | 如果相对应位都是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 |
逻辑运算符
&&都为真,才是真;都为假,才是假。
instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
instanceof运算符使用格式如下:
String name = "James";
boolean result = name instanceof String;
// 由于 name 是 String 类型,所以返回真
如果被比较的对象兼容于右侧类型,该运算符仍然返回 true。
class Vehicle {}
public class Car extends Vehicle {
public static void main(String[] args){
Vehicle a = new Car();
boolean result = a instanceof Car;
System.out.println( result);
}
}
运算符优先级:
Java循环结构
Java中有三种主要的循环结构:(不过多赘述)
- while 循环
- do…while 循环
- for 循环
Java 增强 for 循环
public class Test {
public static void main(String[] args){
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
System.out.print("\n");
String [] names ={"James", "Larry", "Tom", "Lacy"};
for( String name : names ) {
System.out.print( name );
System.out.print(",");
}
}
}
// 输出:
// 10,20,30,40,50,
// James,Larry,Tom,Lacy,
break、continue关键字
- break:跳出循环
- continue:跳过本次迭代
Java条件语句
if-else语句(略)
Java switch-case
如果 case 语句块中没有 break 语句时,匹配成功后,从当前 case 开始,后续所有 case 的值都会输出。
public class Test {
public static void main(String args[]){
int i = 1;
switch(i){
case 0:
System.out.println("0");
case 1: // i = 1,通过case,但由于没有break,后面的case都会运行
System.out.println("1");
case 2:
System.out.println("2");
default:
System.out.println("default");
}
}
}
// 输出:
// 1
// 2
// default
Java Number & Math 类
Java Math 类
Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。
public class Test {
public static void main (String []args)
{
System.out.println("90 度的正弦值:" + Math.sin(Math.PI/2));
System.out.println("0度的余弦值:" + Math.cos(0));
System.out.println("60度的正切值:" + Math.tan(Math.PI/3));
System.out.println("1的反正切值: " + Math.atan(1));
System.out.println("π/2的角度值:" + Math.toDegrees(Math.PI/2));
System.out.println(Math.PI);
}
}
Number & Math 类方法
下面的表中列出的是 Number & Math 类常用的一些方法:
public class NumberAndMathMethodsDemo {
public static void main(String[] args) {
// 1. xxxValue() - 将 Number 对象转换为指定的数据类型
Double d = new Double("100.5");
double value = d.doubleValue();
System.out.println("doubleValue: " + value); // 输出: 100.5
// 2. compareTo() - 比较两个 Number 对象
Integer num1 = 10;
Integer num2 = 20;
int result = num1.compareTo(num2); // 大于 输出1,等于 输出0
System.out.println("compareTo: " + result); // 输出: -1 (num1 < num2)
// 3. equals() - 判断两个 Number 对象是否相等
Integer num3 = 10;
boolean isEqual = num1.equals(num3);
System.out.println("equals: " + isEqual); // 输出: true
// 4. valueOf() - 返回指定字符串的 Number 对象
String strNum = "123";
Integer num4 = Integer.valueOf(strNum);
System.out.println("valueOf: " + num4); // 输出: 123
// 5. toString() - 将 Number 对象转换为字符串
String strNum2 = num4.toString();
System.out.println("toString: " + strNum2); // 输出: "123"
// 6. parseInt() - 将字符串解析为 int 类型
int parsedInt = Integer.parseInt(strNum);
System.out.println("parseInt: " + parsedInt); // 输出: 123
// 7. abs() - 返回参数的绝对值
int absValue = Math.abs(-20);
System.out.println("abs: " + absValue); // 输出: 20
// 8. ceil() - 返回大于等于给定参数的最小整数(双精度浮点型)
double ceilValue = Math.ceil(10.1);
System.out.println("ceil: " + ceilValue); // 输出: 11.0
// 9. floor() - 返回小于等于给定参数的最大整数
double floorValue = Math.floor(10.9);
System.out.println("floor: " + floorValue); // 输出: 10.0
// 10. rint() - 返回与参数最接近的整数(双精度浮点型)
double rintValue = Math.rint(10.5);
System.out.println("rint: " + rintValue); // 输出: 10.0
// 11. round() - 四舍五入,返回最接近的整数
long roundValue = Math.round(10.5);
System.out.println("round: " + roundValue); // 输出: 11
// 12. min() - 返回两个参数中的最小值
int minValue = Math.min(10, 20);
System.out.println("min: " + minValue); // 输出: 10
// 13. max() - 返回两个参数中的最大值
int maxValue = Math.max(10, 20);
System.out.println("max: " + maxValue); // 输出: 20
// 14. exp() - 返回自然数 e 的参数次方
double expValue = Math.exp(1);
System.out.println("exp: " + expValue); // 输出: 2.718281828459045
// 15. log() - 返回参数的自然对数值
double logValue = Math.log(10);
System.out.println("log: " + logValue); // 输出: 2.302585092994046
// 16. pow() - 返回第一个参数的第二个参数次方
double powValue = Math.pow(2, 3);
System.out.println("pow: " + powValue); // 输出: 8.0
// 17. sqrt() - 求参数的算术平方根
double sqrtValue = Math.sqrt(16);
System.out.println("sqrt: " + sqrtValue); // 输出: 4.0
// 18. sin() - 求指定 double 类型参数的正弦值
double sinValue = Math.sin(Math.PI / 2);
System.out.println("sin: " + sinValue); // 输出: 1.0
// 19. cos() - 求指定 double 类型参数的余弦值
double cosValue = Math.cos(0);
System.out.println("cos: " + cosValue); // 输出: 1.0
// 20. tan() - 求指定 double 类型参数的正切值
double tanValue = Math.tan(Math.PI / 4);
System.out.println("tan: " + tanValue); // 输出: 0.9999999999999999
// 21. asin() - 求指定 double 类型参数的反正弦值
double asinValue = Math.asin(1);
System.out.println("asin: " + asinValue); // 输出: 1.5707963267948966
// 22. acos() - 求指定 double 类型参数的反余弦值
double acosValue = Math.acos(0);
System.out.println("acos: " + acosValue); // 输出: 1.5707963267948966
// 23. atan() - 求指定 double 类型参数的反正切值
double atanValue = Math.atan(1);
System.out.println("atan: " + atanValue); // 输出: 0.7853981633974483
// 24. atan2() - 将笛卡尔坐标转换为极坐标,并返回极坐标的角度值
double atan2Value = Math.atan2(1, 1);
System.out.println("atan2: " + atan2Value); // 输出: 0.7853981633974483
// 25. toDegrees() - 将参数从弧度转换为角度
double degreesValue = Math.toDegrees(Math.PI);
System.out.println("toDegrees: " + degreesValue); // 输出: 180.0
// 26. toRadians() - 将角度转换为弧度
double radiansValue = Math.toRadians(180);
System.out.println("toRadians: " + radiansValue); // 输出: 3.141592653589793
// 27. random() - 返回一个随机数
double randomValue = Math.random();
System.out.println("random: " + randomValue); // 输出: 随机数,例如: 0.23456789
}
}
Java Character 类
Character 方法
下面是Character类的方法:(对于方法的完整列表,请参考的 java.lang.Character API 规范。)
public class CharacterMethodsDemo {
public static void main(String[] args) {
// 定义一些字符用于测试
char letterA = 'A';
char digit5 = '5';
char space = ' ';
char lettera = 'a';
char specialChar = '!';
// 1. isLetter() - 判断是否是一个字母
boolean isLetterA = Character.isLetter(letterA);
System.out.println("isLetter('A'): " + isLetterA); // 输出: true
boolean isLetter5 = Character.isLetter(digit5);
System.out.println("isLetter('5'): " + isLetter5); // 输出: false
// 2. isDigit() - 判断是否是一个数字字符
boolean isDigit5 = Character.isDigit(digit5);
System.out.println("isDigit('5'): " + isDigit5); // 输出: true
boolean isDigitA = Character.isDigit(letterA);
System.out.println("isDigit('A'): " + isDigitA); // 输出: false
// 3. isWhitespace() - 判断是否是一个空白字符
boolean isWhitespaceSpace = Character.isWhitespace(space);
System.out.println("isWhitespace(' '): " + isWhitespaceSpace); // 输出: true
boolean isWhitespaceA = Character.isWhitespace(letterA);
System.out.println("isWhitespace('A'): " + isWhitespaceA); // 输出: false
// 4. isUpperCase() - 判断是否是大写字母
boolean isUpperCaseA = Character.isUpperCase(letterA);
System.out.println("isUpperCase('A'): " + isUpperCaseA); // 输出: true
boolean isUpperCasea = Character.isUpperCase(lettera);
System.out.println("isUpperCase('a'): " + isUpperCasea); // 输出: false
// 5. isLowerCase() - 判断是否是小写字母
boolean isLowerCasea = Character.isLowerCase(lettera);
System.out.println("isLowerCase('a'): " + isLowerCasea); // 输出: true
boolean isLowerCaseA = Character.isLowerCase(letterA);
System.out.println("isLowerCase('A'): " + isLowerCaseA); // 输出: false
// 6. toUpperCase() - 转换为大写字母
char upperCaseA = Character.toUpperCase(lettera);
System.out.println("toUpperCase('a'): " + upperCaseA); // 输出: A
char upperCaseSpecial = Character.toUpperCase(specialChar);
System.out.println("toUpperCase('!'): " + upperCaseSpecial); // 输出: !
// 7. toLowerCase() - 转换为小写字母
char lowerCaseA = Character.toLowerCase(letterA);
System.out.println("toLowerCase('A'): " + lowerCaseA); // 输出: a
char lowerCaseSpecial = Character.toLowerCase(specialChar);
System.out.println("toLowerCase('!'): " + lowerCaseSpecial); // 输出: !
// 8. toString() - 返回字符的字符串形式
String stringA = Character.toString(letterA);
System.out.println("toString('A'): " + stringA); // 输出: A
String stringSpace = Character.toString(space);
System.out.println("toString(' '): " + stringSpace); // 输出: " "
}
}
Java String 类★★★★★★
字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。
创建字符串
创建字符串最简单的方式如下:
String str = "Runoob";
在代码中遇到字符串常量时,这里的值是 "Runoob",编译器会使用该值创建一个 String 对象。和其它对象一样,可以使用关键字和构造方法来创建 String 对象。用构造函数创建字符串:
String str2=new String("Runoob");
String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上:
String s1 = "Runoob"; // String 直接创建
String s2 = "Runoob"; // String 直接创建
String s3 = s1; // 相同引用
String s4 = new String("Runoob"); // String 对象创建
String s5 = new String("Runoob"); // String 对象创建
String 类有 11 种构造方法,这些方法提供不同的参数来初始化字符串,比如提供一个字符数组参数:
public class StringDemo{
public static void main(String args[]){
char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'};
String helloString = new String(helloArray);
System.out.println( helloString );
}
}
注意:String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了(详看笔记部分解析)。如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类。
字符串长度★★
用于获取有关对象的信息的方法称为访问器方法。String 类的一个访问器方法是 length() 方法,它返回字符串对象包含的字符数。下面的代码执行后,len 变量等于 14:
public class StringDemo {
public static void main(String args[]) {
String site = "www.runoob.com";
int len = site.length();
System.out.println( "菜鸟教程网址长度 : " + len );
}
}
创建格式化字符串
(专业术语唬人的,理解下面代码就行,嘻嘻。有C语言基础的会很好理解)我们知道输出格式化数字可以使用 printf() 和 format() 方法。
String 类使用静态方法 format() 返回一个String 对象而不是 PrintStream 对象。
String 类的静态方法 format() 能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出。
System.out.printf("浮点型变量的值为 " +
"%f, 整型变量的值为 " +
" %d, 字符串变量的值为 " +
"is %s", floatVar, intVar, stringVar);
你也可以这样写
String fs;
fs = String.format("浮点型变量的值为 " +
"%f, 整型变量的值为 " +
" %d, 字符串变量的值为 " +
" %s", floatVar, intVar, stringVar);
String 方法★★★★★
下面是 String 类支持的方法,更多详细,参看 Java String API 文档:
Java StringBuffer 和 StringBuilder 类
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。(说明:和数组不一样,索引从1开始)
public class RunoobTest{
public static void main(String[] args){
StringBuilder sb = new StringBuilder(10);
sb.append("Runoob..");
System.out.println(sb);
sb.append("!");
System.out.println(sb);
sb.insert(8, "Java"); // 在第8个后面插入
System.out.println(sb);
sb.delete(5,8); // 删除第6、7、8个
System.out.println(sb);
}
}
//Runoob..
//Runoob..!
//Runoob..Java!
//RunooJava!
然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
public class Test{
public static void main(String[] args){
StringBuffer sBuffer = new StringBuffer("菜鸟教程官网:");
sBuffer.append("www");
sBuffer.append(".runoob");
sBuffer.append(".com");
System.out.println(sBuffer);
}
}
// 菜鸟教程官网:www.runoob.com
StringBuffer 方法
以下是 StringBuffer 类支持的主要方法:
序号 | 方法描述 |
---|---|
1 | public StringBuffer append(String s) 将指定的字符串追加到此字符序列。 |
2 | public StringBuffer reverse() 将此字符序列用其反转形式取代。 |
3 | public delete(int start, int end) 移除此序列的子字符串中的字符。 |
4 | public insert(int offset, int i) 将 int 参数的字符串表示形式插入此序列中。 |
5 | insert(int offset, String str) 将 str 参数的字符串插入此序列中。 |
6 | replace(int start, int end, String str) 使用给定 String 中的字符替换此序列的子字符串中的字符。 |
更多内容:
- StringBuffer 类:StringBuffer
- StringBuilder 类:StringBuilder
Java 数组
创建数组:
public class TestArray {
public static void main(String[] args) {
// 定义数组
double[] myList = {1.9, 2.9, 3.4, 3.5};
// 打印所有数组元素
for (int i = 0; i < myList.length; i++) {
System.out.println(myList[i] + " ");
}
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < myList.length; i++) {
total += myList[i];
}
System.out.println("Total is " + total);
// 查找最大元素
double max = myList[0];
for (int i = 1; i < myList.length; i++) {
if (myList[i] > max) max = myList[i];
}
System.out.println("Max is " + max);
}
}
数组作为函数的参数
数组可以作为参数传递给方法。
例如,下面的例子就是一个打印 int 数组中元素的方法:
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
下面例子调用 printArray 方法打印出 3,1,2,6,4 和 2:
printArray(new int[]{3, 1, 2, 6, 4, 2});
数组作为函数的返回值
public static int[] reverse(int[] list) {
int[] result = new int[list.length];
for (int i = 0, j = result.length - 1; i < list.length; i++, j--) {
result[j] = list[i];
}
return result;
}
多维数组
String[][] str = new String[3][4];
String[][] s = new String[2][];
s[0] = new String[2];
s[1] = new String[3];
s[0][0] = new String("Good");
s[0][1] = new String("Luck");
s[1][0] = new String("to");
s[1][1] = new String("you");
s[1][2] = new String("!");
Arrays 类
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
具有以下功能:
- 给数组赋值:通过 fill 方法。
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
- 对数组排序:通过 sort 方法,按升序。
import java.util.Arrays;
public class test {
public static void main(String[] args) {
// 定义一些数组用于测试
int[] array1 = {3, 1, 4, 1, 5, 9};
int[] array2 = {3, 1, 4, 1, 5, 9};
int[] array3 = {2, 7, 1, 8, 2, 8};
// 1. sort - 对指定对象数组根据其元素的自然顺序进行升序排列
Arrays.sort(array1);
System.out.println("sort: " + Arrays.toString(array1)); // 输出: [1, 1, 3, 4, 5, 9]
// 2. binarySearch - 用二分查找算法在给定数组中搜索给定值的对象
// 注意:在使用 binarySearch 之前,数组必须先排序
int index = Arrays.binarySearch(array1, 4);
System.out.println("binarySearch: " + index); // 输出: 3 (因为 4 在排序后的数组中的索引是 2)
// 3. equals - 如果两个指定的 long 型数组彼此相等,则返回 true
boolean isEqual = Arrays.equals(array1, array2);
System.out.println("equals: " + isEqual); // 输出: true (因为 array1 和 array2 内容相同)
boolean isNotEqual = Arrays.equals(array1, array3);
System.out.println("equals: " + isNotEqual); // 输出: false (因为 array1 和 array3 内容不同)
// 4. fill - 将指定的 int 值分配给指定 int 型数组指定范围中的每个元素
Arrays.fill(array3, 0, 3, 0); // 将 array3 的前 3 个元素填充为 0
System.out.println("fill: " + Arrays.toString(array3)); // 输出: [0, 0, 0, 8, 2, 8]
}
}
Java日期时间
Java正则表达式
Java方法
Java 流(Stream)、文件(File)和IO
Java 中的流(Stream)、文件(File)和 IO(输入输出)是处理数据读取和写入的基础设施,它们允许程序与外部数据(如文件、网络、系统输入等)进行交互。
java.io 包是 Java 标准库中的一个核心包,提供了用于系统输入和输出的类,它包含了处理数据流(字节流和字符流)、文件读写、序列化以及数据格式化的工具。
java.io 是处理文件操作、流操作以及低级别 IO 操作的基础包。
java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
读取控制台输入
Java 的控制台输入由 System.in 完成。
为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。
下面是创建 BufferedReader 的基本语法:
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
从控制台读取多字符输入
从 BufferedReader 对象读取一个字符要使用 read() 方法,它的语法如下:
int read( ) throws IOException
每次调用 read() 方法,它从输入流读取一个字符并把该字符作为整数值返回。 当流结束的时候返回 -1。该方法抛出 IOException。
下面的程序示范了用 read() 方法从控制台不断读取字符直到用户输入 q。
//使用 BufferedReader 在控制台读取字符
import java.io.*;
public class BRRead {
public static void main(String[] args) throws IOException {
char c;
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入字符, 按下 'q' 键退出。");
// 读取字符
do {
c = (char) br.read();
System.out.println(c);
} while (c != 'q');
}
}
从控制台读取字符串
从标准输入读取一个字符串需要使用 BufferedReader 的 readLine() 方法。
下面的程序读取和显示字符行直到你输入了单词"end"。
//使用 BufferedReader 在控制台读取字符
import java.io.*;
public class BRReadLines {
public static void main(String[] args) throws IOException {
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str;
System.out.println("Enter lines of text.");
System.out.println("Enter 'end' to quit.");
do {
str = br.readLine();
System.out.println(str);
} while (!str.equals("end"));
}
}
控制台输出
在此前已经介绍过,控制台的输出由 print( ) 和 println() 完成。这些方法都由类 PrintStream 定义,System.out 是该类对象的一个引用。
PrintStream 继承了 OutputStream类,并且实现了方法 write()。这样,write() 也可以用来往控制台写操作。
PrintStream 定义 write() 的最简单格式如下所示:
void write(int byteval)
该方法将 byteval 的低八位字节写到流中。
实例:
下面的例子用 write() 把字符 "A" 和紧跟着的换行符输出到屏幕:
import java.io.*;
//演示 System.out.write().
public class WriteDemo {
public static void main(String[] args) {
int b;
b = 'A';
System.out.write(b);
System.out.write('\n');
}
}
注意:write() 方法不经常使用,因为 print() 和 println() 方法用起来更为方便。
读写文件
如前所述,一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。
下图是一个描述输入流和输出流的类层次图。
字节流(处理二进制数据)
字节流用于处理二进制数据,例如文件、图像、视频等。
类名 | 类型 | 描述 |
---|---|---|
InputStream | 抽象类 (输入流) | 所有字节输入流的超类,处理字节的输入操作。 |
OutputStream | 抽象类 (输出流) | 所有字节输出流的超类,处理字节的输出操作。 |
FileInputStream | 输入流 | 从文件中读取字节数据。 |
FileOutputStream | 输出流 | 将字节数据写入文件。 |
BufferedInputStream | 输入流 | 为字节输入流提供缓冲功能,提高读取效率。 |
BufferedOutputStream | 输出流 | 为字节输出流提供缓冲功能,提高写入效率。 |
ByteArrayInputStream | 输入流 | 将内存中的字节数组作为输入源。 |
ByteArrayOutputStream | 输出流 | 将数据写入到内存中的字节数组。 |
DataInputStream | 输入流 | 允许从输入流中读取 Java 原生数据类型(如 int 、float 、boolean )。 |
DataOutputStream | 输出流 | 允许向输出流中写入 Java 原生数据类型。 |
ObjectInputStream | 输入流 | 从输入流中读取序列化对象。 |
ObjectOutputStream | 输出流 | 将对象序列化并写入输出流中。 |
PipedInputStream | 输入流 | 用于在管道中读取字节数据,通常与 PipedOutputStream 配合使用。 |
PipedOutputStream | 输出流 | 用于在管道中写入字节数据,通常与 PipedInputStream 配合使用。 |
FilterInputStream | 输入流 | 字节输入流的包装类,用于对其他输入流进行过滤处理。 |
FilterOutputStream | 输出流 | 字节输出流的包装类,用于对其他输出流进行过滤处理。 |
SequenceInputStream | 输入流 | 将多个输入流串联为一个输入流进行处理。 |
字符流(处理文本数据)
字符流用于处理文本数据,例如读取和写入字符串或文件。
类名 | 类型 | 描述 |
---|---|---|
Reader | 抽象类 (输入流) | 所有字符输入流的超类,处理字符的输入操作。 |
Writer | 抽象类 (输出流) | 所有字符输出流的超类,处理字符的输出操作。 |
FileReader | 输入流 | 从文件中读取字符数据。 |
FileWriter | 输出流 | 将字符数据写入文件。 |
BufferedReader | 输入流 | 为字符输入流提供缓冲功能,支持按行读取,提高读取效率。 |
BufferedWriter | 输出流 | 为字符输出流提供缓冲功能,支持按行写入,提高写入效率。 |
CharArrayReader | 输入流 | 将字符数组作为输入源。 |
CharArrayWriter | 输出流 | 将数据写入到字符数组。 |
StringReader | 输入流 | 将字符串作为输入源。 |
StringWriter | 输出流 | 将数据写入到字符串缓冲区。 |
PrintWriter | 输出流 | 便捷的字符输出流,支持自动刷新和格式化输出。 |
PipedReader | 输入流 | 用于在管道中读取字符数据,通常与 PipedWriter 配合使用。 |
PipedWriter | 输出流 | 用于在管道中写入字符数据,通常与 PipedReader 配合使用。 |
LineNumberReader | 输入流 | 带行号的缓冲字符输入流,允许跟踪读取的行号。 |
PushbackReader | 输入流 | 允许在读取字符后将字符推回流中,以便再次读取。 |
辅助类(其他重要类)
辅助类提供对文件、目录以及随机文件访问的支持。
类名 | 类型 | 描述 |
---|---|---|
File | 文件和目录操作 | 用于表示文件或目录,并提供文件操作,如创建、删除、重命名等。 |
RandomAccessFile | 随机访问文件 | 支持文件的随机访问,可以从文件的任意位置读写数据。 |
Console | 控制台输入输出 | 提供对系统控制台的输入和输出支持。 |
下面将要讨论的两个重要的流是 FileInputStream 和 FileOutputStream。
FileInputStream
该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
有多种构造方法可用来创建对象。
可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream f = new FileInputStream("C:/java/hello");
也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:
File f = new File("C:/java/hello");
InputStream in = new FileInputStream(f);
创建了 InputStream 对象,就可以使用下面的方法来读取流或者进行其他的流操作。
方法 | 描述 | 示例代码 |
---|---|---|
int read() | 读取一个字节的数据,返回值为 0 到 255 之间的整数。如果到达流的末尾,返回 -1。 | int data = inputStream.read(); |
int read(byte[] b) | 从输入流中读取字节,并将其存储在字节数组 b 中,返回实际读取的字节数。如果到达流的末尾,返回 -1。 | byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer); |
int read(byte[] b, int off, int len) | 从输入流中读取最多 len 个字节,并将它们存储在字节数组 b 的 off 偏移位置,返回实际读取的字节数。如果到达流的末尾,返回 -1。 | byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer, 0, buffer.length); |
long skip(long n) | 跳过并丢弃输入流中的 n 个字节,返回实际跳过的字节数。 | long skippedBytes = inputStream.skip(100); |
int available() | 返回可以读取的字节数(不阻塞)。 | int availableBytes = inputStream.available(); |
void close() | 关闭输入流并释放与该流相关的所有资源。 | inputStream.close(); |
void mark(int readlimit) | 在流中的当前位置设置标记,readlimit 是可以读取的字节数上限。 | inputStream.mark(1024); |
void reset() | 将流重新定位到上次标记的位置,如果没有标记或标记失效,抛出 IOException 。 | inputStream.reset(); |
boolean markSupported() | 检查当前输入流是否支持 mark() 和 reset() 操作。 | boolean isMarkSupported = inputStream.markSupported(); |
除了 InputStream 外,还有一些其他的输入流,更多的细节参考下面链接:
FileOutputStream
该类用来创建一个文件并向文件中写数据。
如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
有两个构造方法可以用来创建 FileOutputStream 对象。
使用字符串类型的文件名来创建一个输出流对象:
OutputStream f = new FileOutputStream("C:/java/hello")
也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:
File f = new File("C:/java/hello");
OutputStream fOut = new FileOutputStream(f);
创建 OutputStream 对象完成后,就可以使用下面的方法来写入流或者进行其他的流操作。
方法 | 描述 | 示例代码 |
---|---|---|
void write(int b) | 将指定的字节写入输出流,b 的低 8 位将被写入流中。 | outputStream.write(255); |
void write(byte[] b) | 将字节数组 b 中的所有字节写入输出流。 | byte[] data = "Hello".getBytes(); outputStream.write(data); |
void write(byte[] b, int off, int len) | 将字节数组 b 中从偏移量 off 开始的 len 个字节写入输出流。 | byte[] data = "Hello".getBytes(); outputStream.write(data, 0, data.length); |
void flush() | 刷新输出流并强制写出所有缓冲的数据,确保数据被立即写入目标输出。 | outputStream.flush(); |
void close() | 关闭输出流并释放与该流相关的所有资源。关闭后不能再写入。 | outputStream.close(); |
除了 OutputStream 外,还有一些其他的输出流,更多的细节参考下面链接:
下面是一个演示 InputStream 和 OutputStream 用法的例子:
import java.io.*;
public class fileStreamTest {
public static void main(String[] args) {
try {
byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
上面的程序首先创建文件test.txt,并把给定的数字以二进制形式写进该文件,同时输出到控制台上。
以上代码由于是二进制写入,可能存在乱码,你可以使用以下代码实例来解决乱码问题:
//文件名 :fileStreamTest2.java
import java.io.*;
public class fileStreamTest2 {
public static void main(String[] args) throws IOException {
File f = new File("a.txt");
FileOutputStream fop = new FileOutputStream(f);
// 构建FileOutputStream对象,文件不存在会自动新建
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
// 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
writer.append("中文输入");
// 写入到缓冲区
writer.append("\r\n");
// 换行
writer.append("English");
// 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
writer.close();
// 关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉
fop.close();
// 关闭输出流,释放系统资源
FileInputStream fip = new FileInputStream(f);
// 构建FileInputStream对象
InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
// 构建InputStreamReader对象,编码与写入相同
StringBuffer sb = new StringBuffer();
while (reader.ready()) {
sb.append((char) reader.read());
// 转成char加到StringBuffer对象中
}
System.out.println(sb.toString());
reader.close();
// 关闭读取流
fip.close();
// 关闭输入流,释放系统资源
}
}
Java Scanner类
Java异常处理
Java 抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。(顾名思义,就是这个类很抽象,不具体,没有足够的信息,故而称抽象类)抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象类
在 Java 语言中使用 abstract class 来定义抽象类。如下实例:略…………
抽象类总结规定
- 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
- 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
Java 接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
- 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
注:JDK 1.8 以后,接口里可以有静态方法和方法体了。
注:JDK 1.8 以后,接口允许包含具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰。更多内容可参考 Java 8 默认方法。
注:JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。更多内容可参考 Java 9 私有接口方法。
接口的声明
接口的声明语法格式如下:
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
Interface关键字用来声明一个接口。下面是接口声明的一个简单例子。
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
public interface NameOfInterface
{
//任何类型 final, static 字段
//抽象方法
}
接口有以下特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
/* 文件名 : Animal.java */
interface Animal {
public void eat();
public void travel();
}
略………………