参考资料:菜鸟教程
不懂问 g 老师 /qw 老师

Q1: .java 是怎么变成可执行文件的?
.java → .class → JVM 运行

  1. 编写.java
  2. javac 把.java 变成.class(平台无关的字节码文件,只能被 JVM 理解和执行)
  3. 通过 JVM 运行:JVM 加载 HelloWorld.class 解释或 JIT(即时编译)成机器码

Q2: 关于 main 方法
每个类都可以有自己的 main 方法,你可以在命令行或 IDE 中选择运行任意一个
java A # 输出: A’s main
java B # 输出: B’s main
但一个程序启动时只能指定一个主类:
java 全限定类名
JVM 只会去这个类里找 main 方法。其他类即使有 main,也不会自动执行。

# JAVA 基础语法

  1. 基本语法
    类名首字母大写,方法名小写字母开头,源文件名必须和类名相同
    所有的 Java 程序由 public static void main(String[] args) 方法开始执行
  2. 标志符
    数字字母下划线和符,不能以数字开头(比c/cpp多了个符,不能以数字开头(比c/cpp多了个符)
  3. 修饰符
    访问控制修饰符: default, public , protected, private
    非访问控制修饰符: final, abstract, static, synchronized
  4. 变量
    局部变量
    类变量(静态变量)
    成员变量(非静态变量)
  5. JAVA 枚举
    enum
    和 c/cpp 的枚举一样
枚举
1
2
3
4
5
6
7
8
9
10
11
class FreshJuice {
enum FreshJuiceSize{ SMALL, MEDIUM , LARGE }
FreshJuiceSize size;
}

public class FreshJuiceTest {
public static void main(String[] args){
FreshJuice juice = new FreshJuice();
juice.size = FreshJuice.FreshJuiceSize.MEDIUM ;
}
}
  1. java 关键字
    看起来和 cpp 差不多,https://www.runoob.com/java/java-basic-syntax.html
  2. 注释
    单行注释 //
    多行注释 /* /
    文档注释 /
    * */,方便被工具提取生成文档
    https://www.runoob.com/java/java-documentation.html
文档注释
1
2
3
4
/*** 这个类绘制一个条形图
* @author runoob
* @version 1.2
*/

# JAVA 对象和类

其他的和 cpp 差不多

  • abstract
    使用抽象类和接口来定义必须实现的方法,不提供具体实现。
    示例:
    抽象类: public abstract class Shape { abstract void draw(); }
    接口: public interface Animal { void eat(); }
  • interface
    定义类必须实现的方法,支持多重继承。
    示例:public interface Drivable
  • method overloading 方法重载
    同一个类中,方法名相同
    在 Java 中,如果你没有显式定义任何构造函数(constructor),编译器会自动为你提供一个 “隐式” 的无参构造函数(也叫默认构造函数),但如果有了带参构造就不会自动创建一个无参构造,此时用无参构造函数会报错

继承中的陷阱(重要!)
子类构造函数必须调用父类的某个构造函数(通过 super (…))。
如果父类没有无参构造函数,而子类又没显式调用 super (…),就会出错

构造
1
2
3
4
5
6
7
8
9
class Parent {
public Parent(String name) { } // 只有带参构造
}

class Child extends Parent {
public Child() {
// 编译器自动加 super(); → 但 Parent 没有无参构造!
}
}

解决方法:显示调用 super("default")

构造
1
2
3
4
5
class Child extends Parent {
public Child() {
super("default"); // 显式调用
}
}

Q: 子类构造函数没有参数,但父类只有带参构造,那父类到底用什么值来构造?
子类在自己的无参构造函数内部,显式传了一个 “固定值” 给父类的带参构造函数。

方法重载
1
2
3
4
5
6
7
8
9
public class MathUtils {
public int add(int a, int b) {
return a + b;
}

public double add(double a, double b) {
return a + b;
}
}

例子:

对象和类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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 语句时,要注意:

  1. 一个源文件中只能有一个 public 类,一个源文件可以有多个非 public 类
  2. 源文件名必须与源文件中 public 类名相同
  3. 如果一个类定义在某个包中,那么 package 语句应该在源文件的首行
    Java 编译器(javac)和 JVM 依赖 “一个 public 类 = 一个 .java 文件” 的约定来快速定位类。
    如果允许多个 public 类在一个文件中,编译器就无法通过类名直接找到对应的源文件
  4. 如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。如果没有 package 语句,那么 import 语句应该在源文件中最前面
    package 声明用于指定当前 Java 类所属的 “命名空间”(namespace),防止类名冲突,并建立代码的逻辑分组和访问控制边界。
源文件声明规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// HelloWorld.java
// 当你编译带包的 Java 文件时,编译器会自动按包名创建目录结构:
package com.example.hello; // 包声明

public class HelloWorld { }
/*
执行javac -d out HelloWorld.java编译
编译之后的结构
out/
└── com/
└── example/
└── hello/
└── HelloWorld.class
*/
  1. import 语句和 package 语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明
import
1
2
import java.util.ArrayList;
import com.example.util.StringUtils;

让当前类在代码中可以直接使用其他包中的类,而不用写全限定名,没有 import 的话就得:

import
1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
java.util.ArrayList<String> list = new java.util.ArrayList<>();
com.example.util.StringUtils.isEmpty("hello");
}
}

有了 import 之后:

import
1
2
3
4
5
6
7
8
9
import java.util.ArrayList;
import com.example.util.StringUtils;

public class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); // 不用写 java.util.
StringUtils.isEmpty("hello"); // 不用写 com.example.util.
}
}
  1. import 只是让编译器知道某个简短名字(如 ArrayList )对应哪个全名( java.util.ArrayList )。类的实际加载是由 JVM 在运行时按需完成的,和 import 无关。
  2. 同一包内的类可以直接使用,无需 import
  3. 可以 import 某个包下的所有类: import com.example.util.*;
    或者 import 某个类: import com.example.util.StringUtils;
  4. 在 Java 中,如果一个 .java 文件没有 package 语句,那么它就属于 “默认包”(unnamed package /default package)。所有在默认包中的类,彼此之间可以直接访问,无需 import

# Java 基本数据类型

# 不一样的:bool->boolean

java 的成员变量都有默认初始值

数据类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char ‘\u0000’
boolean false
引用类型 null

Java 的局部变量(在方法、构造函数或代码块中定义的变量)如果没有显式初始化,编译器会报错 —— 不允许使用未初始化的局部变量。(c/cpp 没有初始化会是一个随机的垃圾值)

引用类型:相当于指针(不是引用!!!)

引用类型
1
Person p = new Person("Alice");

引用类型变量 p 的值是 Person 对象的地址,而不是对象本身。
种类:

  1. 类类型
类类型
1
2
3
String name = "Alice";           // String 是类
Scanner sc = new Scanner(...); // 自定义或标准库类
MyClass obj = new MyClass(); // 你自己写的类
  1. 接口类型
接口类型
1
2
List<String> list = new ArrayList<>(); // List 是接口,ArrayList 是实现类
Runnable task = () -> System.out.println("Hello");
  1. 数组类型
    数组本身总是引用类型
数组类型
1
2
int[] numbers = new int[5];      // 数组是引用类型,元素是基本类型
String[] names = {"Alice", "Bob"}; // 数组和元素都是引用类型
  1. 枚举类型
枚举类型
1
2
enum Color { RED, GREEN, BLUE }
Color c = Color.RED; // Color 是引用类型
  1. 引用类型
    特性:
  • 默认为 null
  • 赋值是 “共享引用”
“共享引用”
1
2
3
4
5
Person p1 = new Person("Alice");
Person p2 = p1; // p2 和 p1 指向同一个对象!

p2.setName("Bob");
System.out.println(p1.getName()); // 输出 "Bob"!
  • 使用 == 比较的是引用地址,不是内容
使用
1
2
3
4
5
String a = new String("hello");
String b = new String("hello");

System.out.println(a == b); // false(不同对象)
System.out.println(a.equals(b)); // true(内容相同)
  • 方法参数传递是 “按值传递”,但对于引用类型,传递的是引用的副本(不是对象本身,也不是引用本身)
传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void changeName(Person p) {
p.setName("Charlie"); // 修改对象内容 → 外部可见
p = new Person("David"); // 重新赋值引用 → 外部不可见
}

Person x = new Person("Alice");
changeName(x);
/*
栈(Stack) 堆(Heap)
┌─────┐ ┌──────────────────────┐
│ x │──────────────────────▶│ Person{name="Alice"} │
├─────┤ └──────────────────────┘
│ p │──────────────────────▶↑
└─────┘ (p 和 x 指向同一个对象!)
---------------------------------------------------------
栈(Stack) 堆(Heap)
┌─────┐ ┌──────────────────────┐
│ x │──────────────────────▶│ Person{name="Charlie"} │ ← 被修改了!
├─────┤ └──────────────────────┘
│ p │──────────────────────▶↑
└─────┘
---------------------------------------------------------
栈(Stack) 堆(Heap)
┌─────┐ ┌──────────────────────┐
│ x │──────────────────────▶│ Person{name="Charlie"} │
├─────┤ └──────────────────────┘
│ p │──────────┐
└─────┘ ▼
┌──────────────────────┐
│ Person{name="David"} │
└──────────────────────┘
只有 p 改变了指向,x 仍然指向原来的对象,而 p 是局部变量,方法结束后就销毁了
*/
System.out.println(x.getName()); // 输出 "Charlie"

# 常量

在 Java 中使用 final 关键字来修饰常量

常量
1
final double PI = 3.1415927;

# 类型转换

# 自动转

低的转高的

低 ------------------------------------> 高

byte,short,char—> int —> long—> float —> double

# 强制转

强制转换
1
2
(int)23.7 == 23;        
(int)-45.89f == -45

# Java 变量类型

  • 局部变量:和 cpp 不一样的是不初始化会导致编译错误,在栈上,生命周期结束会被 JVM 自动回收。和 c/cpp 一样
  • 静态变量 / 类变量:类变量在类内用 static 修饰,所有这个类的对象共享这个变量
  • 实例变量:类不是 static 的成员变量(?)没有初始化会被赋予默认值(见上),需要考虑线程安全
  • 参数变量:形参,值传递和引用传递

# Java 修饰符

  1. 访问修饰符
    default (即默认,什么也不写): 在同一包内可见,不同包内不可见,不使用任何修饰符。
    private: 在同一类内可见。不能修饰类(不在其他类里面的类)
    public: 对所有类可见。
    protected: 对同一包内的类和所有子类可见。使用对象:变量、方法。 同样不能修饰不在其他类里面的类。
修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他类(不同包)
public Y Y Y Y Y
private Y N N N N
protected Y Y Y 需要说明 N
default Y Y Y N N

protected
https://cloud.tencent.com/developer/article/1333550

  1. 非访问修饰符
  • static: 静态修饰符,静态成员变量和静态方法,静态块,静态内部类。静态方法和静态成员变量一样也是类所有的,不能使用类的非静态变量(实例),只能访问静态成员变量等
  • final: final 修饰的类不能够被继承,修饰的方法不能被继承类 override,修饰的变量为常量,是不可修改的
  • abstruct: 抽象类(不能被实例化,只能被继承)和抽象方法(没有具体的实现,只能被 override)类似于纯虚函数
  • synchronized: 方法同一时间只能被一个线程访问
  • translient: transient 修饰的变量不会被序列化(反序列化出来没有该变量)
  • volatile: 同 c/cpp

#

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

NoResponse WeChat Pay

WeChat Pay

NoResponse Alipay

Alipay

NoResponse PayPal

PayPal