参考资料:菜鸟教程
不懂问 g 老师 /qw 老师
Q1: .java 是怎么变成可执行文件的?
.java → .class → JVM 运行
编写.java
javac 把.java 变成.class(平台无关的字节码文件,只能被 JVM 理解和执行)
通过 JVM 运行:JVM 加载 HelloWorld.class 解释或 JIT(即时编译)成机器码
Q2: 关于 main 方法
每个类都可以有自己的 main 方法,你可以在命令行或 IDE 中选择运行任意一个
java A # 输出: A’s main
java B # 输出: B’s main
但一个程序启动时只能指定一个主类:
java 全限定类名
JVM 只会去这个类里找 main 方法。其他类即使有 main,也不会自动执行。
# JAVA 基础语法
基本语法
类名首字母大写,方法名小写字母开头,源文件名必须和类名相同
所有的 Java 程序由 public static void main(String[] args) 方法开始执行
标志符
数字字母下划线和符,不能以数字开头(比 c / c p p 多了个 符,不能以数字开头(比c/cpp多了个 符 , 不 能 以 数 字 开 头 ( 比 c / c p p 多 了 个 符)
修饰符
访问控制修饰符: default, public , protected, private
非访问控制修饰符: final, abstract, static, synchronized
变量
局部变量
类变量(静态变量)
成员变量(非静态变量)
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 ; } }
java 关键字
看起来和 cpp 差不多,https://www.runoob.com/java/java-basic-syntax.html
注释
单行注释 //
多行注释 /* /
文档注释 / * */,方便被工具提取生成文档
https://www.runoob.com/java/java-documentation.html
文档注释
# 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("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); } public void setAge (int age) { this .age = age; } public int getAge () { return age; } public String getName () { return name; } public static void main (String[] args) { Puppy myPuppy = new Puppy ("Tommy" ); myPuppy.setAge(2 ); int age = myPuppy.getAge(); System.out.println("小狗的年龄为 : " + age); System.out.println("变量值 : " + myPuppy.getAge()); } }
# 源文件声明规则
在一个源文件中定义多个类,并且还有 import 语句和 package 语句时,要注意:
一个源文件中只能有一个 public 类,一个源文件可以有多个非 public 类
源文件名必须与源文件中 public 类名相同
如果一个类定义在某个包中,那么 package 语句应该在源文件的首行
Java 编译器(javac)和 JVM 依赖 “一个 public 类 = 一个 .java 文件” 的约定来快速定位类。
如果允许多个 public 类在一个文件中,编译器就无法通过类名直接找到对应的源文件
如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。如果没有 package 语句,那么 import 语句应该在源文件中最前面
package 声明用于指定当前 Java 类所属的 “命名空间”(namespace),防止类名冲突,并建立代码的逻辑分组和访问控制边界。
源文件声明规则 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.example.hello; public class HelloWorld { }
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 <>(); StringUtils.isEmpty("hello" ); } }
import 只是让编译器知道某个简短名字(如 ArrayList )对应哪个全名( java.util.ArrayList )。类的实际加载是由 JVM 在运行时按需完成的,和 import 无关。
同一包内的类可以直接使用,无需 import
可以 import 某个包下的所有类: import com.example.util.*;
或者 import 某个类: import com.example.util.StringUtils;
在 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 2 3 String name = "Alice" ; Scanner sc = new Scanner (...); MyClass obj = new MyClass ();
接口类型
接口类型 1 2 List<String> list = new ArrayList <>(); Runnable task = () -> System.out.println("Hello" );
数组类型
数组本身总是引用类型
数组类型 1 2 int [] numbers = new int [5 ]; String[] names = {"Alice" , "Bob" };
枚举类型
枚举类型 1 2 enum Color { RED, GREEN, BLUE }Color c = Color.RED;
引用类型
特性:
“共享引用” 1 2 3 4 5 Person p1 = new Person ("Alice" );Person p2 = p1; p2.setName("Bob" ); System.out.println(p1.getName());
使用 1 2 3 4 5 String a = new String ("hello" );String b = new String ("hello" );System.out.println(a == b); System.out.println(a.equals(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 void changeName (Person p) { p.setName("Charlie" ); p = new Person ("David" ); } Person x = new Person ("Alice" );changeName(x); System.out.println(x.getName());
# 常量
在 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 修饰符
访问修饰符
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
非访问修饰符
static: 静态修饰符,静态成员变量和静态方法,静态块,静态内部类。静态方法和静态成员变量一样也是类所有的,不能使用类的非静态变量(实例),只能访问静态成员变量等
final: final 修饰的类不能够被继承,修饰的方法不能被继承类 override,修饰的变量为常量,是不可修改的
abstruct: 抽象类(不能被实例化,只能被继承)和抽象方法(没有具体的实现,只能被 override)类似于纯虚函数
synchronized: 方法同一时间只能被一个线程访问
translient: transient 修饰的变量不会被序列化(反序列化出来没有该变量)
volatile: 同 c/cpp