Java_day01
Java基础
Java简介
Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称。由 James Gosling和同事们共同研发,并在 1995 年正式推出。
后来 Sun 公司被 Oracle (甲骨文)公司收购,Java 也随之成为 Oracle 公司的产品。
Java分为三个体系:
- JavaSE(J2SE)(Java2 Platform Standard Edition,java平台标准版)
- JavaEE(J2EE)(Java 2 Platform,Enterprise Edition,java平台企业版)
- JavaME(J2ME)(Java 2 Platform Micro Edition,java平台微型版)。
主要特性
- Java 语言是简单的
- Java 语言是面向对象的
- Java语言是分布式的
- Java 语言是健壮的
- Java语言是安全的
- Java 语言是体系结构中立的
- Java 语言是可移植的
- Java 语言是解释型的
- Java 是高性能的
- Java 语言是多线程的
- Java 语言是动态的
下载JDK
通过链接Java Downloads | Oracle下载

下载好后,按照提示一步步安装

选择安装位置

关闭即可

验证安装
win+R输入cmd回车打开运行框输入: java –version , 返回下方结果就是安装好了

安装IDEA

单击Download开始下载windows版本的IDEA

选择安装位置

按需要勾选

单击安装即可

等待安装完成

创建一个Java项目


选好jdk后创建

然后就会创建一个有示范代码的java项目

Hello World程序
在src目录下新建目录,创建一个Java类

输入类名Hello

输入下方代码
package java_day01;
public class Hello {
public static void main(String[] args){
System.out.println("Hello,Java");
}
}
执行输入Hello , Java

基本语法
编写 Java 程序时,应注意以下几点:
- 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。
- 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的 Java 程序由 public static void main(String[] args) 方法开始执行。
Java 标识符
Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
关于 Java 标识符,有以下几点需要注意:
- 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
- 关键字不能用作标识符
- 标识符是大小写敏感的
- 合法标识符举例:age、$salary、_value、__1_value
- 非法标识符举例:123abc、-salary
枚举类型
Java 5.0引入了枚举,枚举限制变量只能是预先设定好的值。使用枚举可以减少代码中的 bug。
package java_day01;
class FreshJuice {
enum FreshJuiceSize{ SMALL, MEDIUM , LARGE }
FreshJuiceSize size;
}
public class Enum {
public static void main(String[] args){
FreshJuice juice = new FreshJuice();
juice.size = FreshJuice.FreshJuiceSize.MEDIUM ;
System.out.println(juice.size);
}
}
关键字
abstract
java语言常用关键字:
- abstract 用于声明一个抽象类或抽象方法
抽象类
抽象类使用 abstract 关键字声明。抽象类可以包含普通的方法和属性,也可以包含抽象方法。抽象方法是没有具体实现的方法,它只有方法名,没有方法体。抽象类的语法如下:
public abstract class MyClass {
// 抽象类的属性和方法
}
抽象类的一个示例:
public abstract class Shape {
protected int x;
protected int y;
public Shape(int x, int y) {
this.x = x;
this.y = y;
}
public abstract double getArea();
}
在这个例子中,Shape 是一个抽象类,它包含一个抽象方法 getArea()。
抽象方法
抽象方法使用 abstract 关键字声明,没有方法体,只有方法的声明。抽象方法必须在子类中被实现,否则子类也必须被声明为抽象类。抽象方法的语法如下:
public abstract void myMethod();
抽象方法的一个示例:
public abstract class Shape {
public abstract double getArea();
}
子类实现抽象方法
子类继承抽象类时,必须实现抽象类中的所有抽象方法。例如:
public class Circle extends Shape {
private int radius;
public Circle(int x, int y, int radius) {
super(x, y);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
在这个例子中,Circle 类继承了 Shape 类,并实现了 getArea() 方法。
使用抽象类和抽象方法的好处
使用抽象类和抽象方法可以让我们更好地组织代码,实现多态和代码复用,同时也可以在设计上保证代码的可维护性和可扩展性。
注意事项
- 抽象类不能被实例化。
- 抽象类可以包含构造方法。
- 抽象类中不一定包含抽象方法,但包含抽象方法的类一定是抽象类。
- 抽象方法不能有方法体,必须在子类中实现。
通过理解和使用 abstract 关键字,可以更好地设计和实现面向对象的程序,提高代码的灵活性和可维护性。
native
在Java编程中,native关键字用于声明一个方法是由本地代码(通常是C或C++)实现的。这意味着该方法的具体实现不是用Java编写的,而是由底层的本地代码提供。这种方法允许Java程序与本地代码进行交互,实现更高效的操作,或者访问底层系统资源。
要声明一个native方法,只需在Java方法的声明中加上native关键字,并且不需要提供方法的实现。例如:
public native void nativeMethod();
在这个例子中,nativeMethod()是一个native方法,它的具体实现将在本地代码中提供。
为了使用native方法,必须在Java程序中加载本地库,并确保本地库中包含了所需的函数。本地库可以使用Java的JNI(Java Native Interface)来编写,并在程序运行时通过System.loadLibrary()方法加载。下面是一个简单的示例:
public class NativeExample {
static {
System.loadLibrary("nativeLibrary");
}
public native void nativeMethod();
public static void main(String[] args) {
new NativeExample().nativeMethod();
}
}
在这个示例中,nativeMethod()将在名为nativeLibrary的本地库中实现。
实现步骤
- 创建包含本地方法的类:编写一个包含native方法的Java类。
- 编译运行:使用javac编译Java类。
- 获得头文件:使用javah生成包含本地方法声明的头文件。
- 实现头文件的声明方法:在C或C++中实现头文件中声明的方法。
- 生成动态链接库:将C或C++文件编译为动态链接库(如DLL文件)。
- 再次运行Java类:使用System.loadLibrary()加载动态链接库,并运行Java类。
注意事项
使用native方法虽然可以提高程序的性能,但也带来了潜在的安全隐患和跨平台兼容性问题。因此,除非必要,否则应尽量避免使用native方法。
throw
在 Java 中,throw 关键字用于显式地抛出一个异常。通过使用 throw 语句,程序员可以在代码中手动引发异常,从而中止当前的执行流,并将控制权交给相应的 catch 块来处理该异常。
throw 语句的基本语法如下:
throw new ExceptionInstance;
其中,ExceptionInstance 是一个异常类的实例。需要注意的是,throw 语句每次只能抛出一个异常实例。
以下是一个使用 throw 语句抛出异常的示例:
class Main {
public static void divideByZero() {
throw new ArithmeticException("试图除以0");
}
public static void main(String[] args) {
divideByZero();
}
}
输出结果:
Exception in thread "main" java.lang.ArithmeticException: 试图除以0
at Main.divideByZero(Main.java:3)
at Main.main(Main.java:7)
在这个示例中,我们显式地抛出了一个 ArithmeticException 异常。
抛出检查异常
对于检查异常(Checked Exception),必须在方法声明中使用 throws 关键字来声明该方法可能抛出的异常类型。例如:
import java.io.*;
class Main {
public static void findFile() throws IOException {
throw new IOException("文件未找到");
}
public static void main(String[] args) {
try {
findFile();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
输出结果:
文件未找到
在这个示例中,findFile() 方法使用传递给其构造函数的消息抛出了一个 IOException。由于它是一个检查异常,因此必须在 throws 子句中指定它。
自定义异常类
在大多数情况下,创建自定义异常类可以帮助更好地描述和处理特定的异常情况。以下是一个自定义异常类的示例:
class AuctionException extends Exception {
public AuctionException(String message) {
super(message);
}
}
通过创建自定义异常类,可以在代码中更准确地描述异常。
总结
通过使用 throw 关键字,Java 程序员可以显式地抛出异常,从而中止当前的执行流,并将控制权交给相应的 catch 块来处理该异常。对于检查异常,必须在方法声明中使用 throws 关键字来声明该方法可能抛出的异常类型。自定义异常类可以帮助更好地描述和处理特定的异常情况。
strictfp
在Java中,strictfp关键字用于确保浮点计算的结果在不同的处理器平台上保持一致。由于不同处理器可能使用不同的浮点寄存器(例如80位和64位),这可能导致浮点计算结果的不一致。使用strictfp关键字可以强制Java虚拟机按照IEEE 754浮点标准进行计算,从而保证结果的一致性。
使用示例
以下是一个使用strictfp关键字修饰方法的示例:
public class StrictfpTest {
public static strictfp void main(String[] args) {
float aFloat = 0.6710339f;
double aDouble = 0.04150553411984792d;
double sum = aFloat + aDouble;
float quotient = (float)(aFloat / aDouble);
System.out.println("float: " + aFloat);
System.out.println("double: " + aDouble);
System.out.println("sum: " + sum);
System.out.println("quotient: " + quotient);
}
}
在这个例子中,main方法中的所有指令都将使用严格的浮点计算。
适用范围
strictfp关键字可以用来修饰类、接口或方法。使用strictfp关键字标记的方法必须使用严格的浮点计算来生成可再生的结果。严格的浮点计算表示浮点计算完全依照浮点规范IEEE 754来执行。
性能影响
需要注意的是,采用strictfp关键字会对中间结果进行截断操作,而截断操作需要消耗时间,因此在计算速度上比不使用strictfp要慢。然而,对于大多数程序来说,浮点溢出并不是一个大问题。
结论
尽管strictfp关键字在Java 17中已经变得不再必要,但它在确保浮点计算结果一致性方面仍然具有重要意义。
transient
transient 是 Java 中用于控制序列化行为的关键字。它的主要作用是阻止某个实例变量被序列化,即在对象持久化时,该字段不会被写入到序列化流中。
当一个类实现了 Serializable 接口时,默认所有非静态成员变量都会被序列化。但在实际开发中,有些字段(如密码、银行卡号等敏感信息)不希望被持久化或通过网络传输,这时就可以用 transient 修饰。
基本用法示例
import java.io.*;
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private transient String password; // 不会被序列化
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String toString() {
return "username: " + username + ", password: " + password;
}
}
public class TransientDemo {
public static void main(String[] args) throws Exception {
User user = new User("Alexia", "123456");
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.obj"));
oos.writeObject(user);
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.obj"));
User deserialized = (User) ois.readObject();
ois.close();
System.out.println(deserialized);
}
}
运行结果中 password 会变为 null,说明它未被序列化。
注意事项
- 仅作用于变量:不能修饰方法、类,本地变量也不能用 transient。
- 静态变量不受影响:静态字段本身不参与序列化,与是否加 transient 无关。反序列化后静态变量值来自当前 JVM,而非序列化数据。
- Externalizable 特例:如果类实现的是 Externalizable 接口,并在 writeExternal 方法中手动序列化 transient 字段,该字段依然会被写入。
适用场景
- 屏蔽敏感信息(密码、密钥等)
- 避免序列化无意义或不可序列化的对象(如线程、Socket 等)
- 减少序列化数据体积,提高传输效率
这样,transient 可以帮助我们在序列化过程中更好地控制数据安全与性能。
protected
在 Java 中,protected 是一种访问修饰符,用于控制类成员(字段、方法、构造函数)的可见性。它的主要作用是允许子类和同一包内的类访问被修饰的成员,但在不同包的非子类中不可见。
protected 的可见性规则
- 同一包内:protected 修饰的成员对同一包内的所有类可见。
- 子类访问:即使子类与父类不在同一包中,子类也可以访问从父类继承的 protected 成员,但只能通过子类自身的实例访问,而不能通过父类实例或其他子类实例访问。
- 跨包限制:如果父类和子类不在同一包中,父类无法访问子类中特有的 protected 成员。
示例代码
以下是一个关于 protected 的简单示例,展示其在不同场景下的行为:
// 父类在包 A 中
package A;
public class Parent {
protected void display() {
System.out.println("Protected method in Parent class");
}
}
// 子类在包 B 中
package B;
import A.Parent;
public class Child extends Parent {
public void callDisplay() {
// 子类可以访问继承的 protected 方法
display();
}
}
// 测试类在包 B 中
package B;
public class Test {
public static void main(String[] args) {
Child child = new Child();
child.callDisplay(); // 输出: Protected method in Parent class
Parent parent = new Parent();
// parent.display(); // 编译错误:无法通过父类实例访问 protected 方法
}
}
注意事项
- 跨包访问限制:如果子类和父类不在同一包中,子类只能通过自身实例访问继承的 protected 成员,不能通过父类实例或其他子类实例访问。
- 构造函数的 protected 修饰:protected 构造函数允许子类调用,但限制了非子类直接实例化父类。
- 内部类的 protected 修饰:protected 的内部类只能在同一包内或子类中被访问,但其默认构造函数的可见性也会受到限制。
总结
protected 是 Java 中较为复杂的访问修饰符之一。它在包内和子类中提供了灵活的访问权限,但在跨包场景中有严格的限制。理解其规则有助于在设计类的继承关系时更好地控制访问权限。
synchronized
synchronized 是Java内置的线程同步机制,用于在多线程环境下保证原子性、可见性和有序性。它通过**对象监视器锁(Monitor)**来控制多个线程对共享资源的互斥访问,防止竞态条件。
核心作用
- 原子性:确保同步代码块在同一时刻只被一个线程执行。
- 可见性:线程释放锁前会将工作内存数据刷新到主内存,其他线程获取锁时会读取最新值。
- 有序性:禁止同步块内的指令重排序,符合JMM的happens-before规则。
常用用法
- 修饰实例方法(锁对象为this)
public synchronized void deposit(double amount) {
balance += amount;
}
适用于保护实例变量,多个实例互不影响。
- 修饰静态方法(锁对象为Class对象)
public static synchronized void increment() {
count++;
}
适用于类级别共享资源,所有实例共用一把锁。
- 同步代码块(可指定任意锁对象)
private final Object lock = new Object();
public void update() {
synchronized (lock) {
// 仅同步关键代码
}
}
可细化锁粒度,减少锁竞争。
底层原理
- 编译后生成monitorenter和monitorexit字节码指令。
- JVM通过对象头的Mark Word记录锁状态(无锁、偏向锁、轻量级锁、重量级锁)。
- 锁升级过程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,升级不可逆。
- JDK 1.6后引入偏向锁和轻量级锁,大幅优化性能。
性能优化建议
- 减小锁粒度:拆分锁对象,允许并行访问不同资源。
- 缩短锁持有时间:将耗时操作移出同步块。
- 锁消除:利用JVM逃逸分析移除不必要的锁。
- 锁粗化:合并连续的加锁操作,减少锁获取次数。
常见问题
- 死锁:多个线程循环等待对方持有的锁,可通过固定加锁顺序或tryLock避免。
- 错误锁对象:使用可变对象或字符串常量池对象作为锁可能导致同步失效,应使用final独立锁对象。
- 高竞争性能瓶颈:可用Atomic类(CAS乐观锁)、ReadWriteLock或并发容器替代。
与Lock接口对比
- synchronized:隐式获取/释放锁,不可中断,不支持超时,代码简洁,JVM自动优化。
- Lock:显式获取/释放锁,可中断、可超时、可公平锁,支持多个条件变量,适合复杂同步需求。
示例:同步代码块防止竞态
public class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
public int getCount() {
synchronized (lock) {
return count;
}
}
}
该实现确保count在多线程环境下安全递增。
synchronized简单易用,适合大多数线程安全需求;在高并发或复杂控制场景下,可结合Lock或无锁结构提升性能。
volatile
volatile 是Java中的轻量级同步机制,主要用于保证多线程环境下共享变量的可见性以及防止指令重排序,但不保证原子性。它通过在底层插入**内存屏障(Memory Barrier)**来实现语义,确保一个线程对变量的修改能立即被其他线程看到,并禁止特定类型的重排序。
核心特性:
- 可见性:线程写入volatile变量会立即刷新到主内存,其他线程读取时直接从主内存获取最新值。
- 有序性:禁止与volatile变量相关的指令重排序,保证执行顺序符合预期。
- 无原子性:复合操作(如i++)仍需加锁或使用原子类。
典型应用场景:
- 状态标记(线程停止信号)
- 双重检查锁定单例模式
- 安全发布对象
示例:状态标记
public class ShutdownHook extends Thread {
private volatile boolean shutdownRequested = false;
public void run() {
while (!shutdownRequested) {
// 业务逻辑
}
}
public void shutdown() {
shutdownRequested = true;
}
}
此处volatile确保shutdownRequested的修改对所有线程立即可见。
示例:双重检查锁定单例
public class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile防止对象初始化过程的指令重排,避免其他线程获取到未完全初始化的对象。
与synchronized对比:
- volatile:仅保证可见性和有序性,无锁,高性能,适合简单状态标记。
- synchronized:保证可见性+原子性+互斥性,有锁,适合复杂同步逻辑。
注意事项:
- 不适用于需要原子性的场景(如计数器累加)。
- 适合修饰布尔、引用等简单类型。
- 过度使用会影响性能,应在确有可见性或重排序风险时使用。
总结:volatile是并发编程中解决可见性和有序性问题的利器,配合happens-before规则可构建高效的线程安全机制,但需结合锁或原子类来处理原子性需求。
常见关键字
| class | const | break | byte | super |
| enum | extends | continuee | default | try |
| if | implements | final | finally | case |
| short | new | import | instanceof | do |
| boolean | static | package | private | float |
| int | switch | void | catch | double |
| for | interface | public | char | else |
| goto | long | return | this | while |
说明: true,false,null等单词在Java语言中不属于关键字,而属于保留字,它们也不能被用来命名标识符。
标识符命名规则
- 标识符可以由多个单词连接而成 如 photoprice
- 类名的命名:每个单词的首字母要大写,其他字母小写。 如 TeacherHelloWorld
- 方法名和变量名:如果名称只有一个单词就全小写,如果由多个单词组成,除了第一个单词以外其他单词都首字母大写 如stuName
- 常量名:所有字母大写,多个单词用_连接 如 PAI MIN_VALUE
- 包名:所有单词的所有字母都小写 如 com.example








