Java 学习
发布于 2026-04-06更新于 2026-11-1411801 字76 分钟 · 阅读时间
2024-2-27~2024-3-13
每个牛 B 人物背后都有段苦逼的岁月,只要像 sb 的一样坚持,终将牛 B!
2026-4-6 :当时好像看的是狂神说的
特性与优势
| 简单性 | 面向对象 | 可移植性 |
|---|---|---|
| 高性能 | 分布式 | 动态性(反射的特性) |
| 多线程 | 安全性(内存问题少) | 健壮性(用的人多) |
分为三个版本
要先学好标准版才行
JDK、JRE、JVM
JDK:Java Development Kit (Java 开发者工具)(包含下两)
JRE:Java Runtime Environment (Java 运行时环境)(包含 JVM)
JVM:Java Virtual Machine (Java 虚拟机)

先新建一个文件,改为Hello.java 然后编写代码
public class Hello { public static void main(String[] args) { System.out.println("Hello,World!"); }}之后通过 cmd 命令行,使用 javac 编译文件,然后使用 java 运行文件
javac Hello.javajava Hello# 输出:Hello,World!(当然也可以使用 VSCode 自带的运行功能在命令行中运行)
System.out.print("Hello, world!"); // 输出System.out.println("Hello,World!"); // 输出+换行System.out.printf("Hello, %s!", "world"); // C语言的输出方式
就是先编译成字节码,然后在通过解释器解释,介于编译型与解释型两者之间的语言
因为是因为 C 语言的影响设计的,当时制作 java 时就是想做一个语法像 C 语言,又没有 C++复杂的机制的语言,于是 Java 诞生了(刚开始还想叫 C++--),所以和 C 语言一样的我就不多写了
// 单行注释/* 多行注释 *//** * 文本注释 */标识符还可以使用美元符号 $ 和 中文字符
int $cc = 0;String 中文¥="1";| abstract | assert | boolean | break | byte |
| case | catch | char | class | const |
| continue | default | do | double | else |
| enum | extends | final | finally | float |
| for | goto | if | implements | import |
| instanceof | int | interface | long | native |
| new | package | private | protected | public |
| return | strictfp | short | static | super |
| switch | synchronized | this | throw | throws |
| transient | try | void | volatile | while |
| C++ | Java | 值范围/其他 |
|---|---|---|
| byte(整数) | -128~127 | |
| int(32 位)/ short(64 位) |
short | -32768~32767 |
| int(64 位)/ long |
int | -2,147,483,648 ~ 2,147,483,647 (2*10^9^) |
| long long | long | -9223372036854775808 ~ 9223372036854775807 (9*10^18^) |
| char(1 字节) | char(2 字节) | Java 默认使用 Unicode 字符集,包括 UTF-8 等。C++使用的是 ASCII 字符集 |
| string | String | 注:string/String 是类,而不是基本数据类型 |
| bool | boolean |
java 分为基本数据类型与引用类型(对象),引用类型分为
java 对于long类型和float类型,有点不同,
对于long类型,如果long类型的数后面不加一个L,会被编译器识别成int类型,会报错 “整数过大”,要在后面加一个 L 才会识别成long类型,从而不报错;
对于float类型的话默认会变成double类型,如果想要变成float类型,要在后面加一个F
// C++char n1 ='阿'; // ×char n2[]="阿"; // √string n3="阿"; // √ // Javachar n4 ='阿'; // √String n5="阿"; // √和 C++一样,可以使用特定的前缀来表示不同进制
| 进制 | C++/Java |
|---|---|
| 二进制 | 0b1010 |
| 八进制 | 012 |
| 十进制 | 10 |
| 十六进制 | 0xA |
数字还有前缀中的字母也是不分大小写的
| C++ | Java | |
|---|---|---|
| >> | >> | 右移一位,左边填充与最左边的位相同的值 |
| >>> | 右移一位,左边全填充 0 | |
| instanceof | A instanceof B 用于检查 变量名 A 是否属于对象 B(引用类型 B) (instance + of) |
// 按照顺序运算的System.out.println(""+(10+20)); // 输出:30System.out.println(""+10+20); // 输出:1020System.out.println(10+20+""); // 输出:30包(软件包) 就是 文件夹 ,像是一个文件夹中无法放置两个相同名字的文件,但只要将这两个文件分别放到两个文件夹中就可以用同一个名字了
包机制就可以用同样的方法更好的组织类,用于区别类名的命名空间,还可以分类的整整齐齐
一般利用公司域名倒置作为包名;如 com.baidu.www
三个文件夹: com baidu www以下例子要用的路径
- src - com - myapp - Helper.java - Main.java - main.cpp - util - Document.java - lib - mylib - MyLibrary.java - sakura.h包语句的语法格式
package pkg1[.pkg2[.pkg3…]];// 用于声明一个类所在的包(要从类路径开始写包所在的路径)// 点. 表示子包// 类路径:包含顶级包com和lib的文件夹(就是上面的src)package com.myapp; // 声明Main在"com/myapp"下 public class Main { // ...}导入包的语句
import pkg1[.pkg2…].(classname|*);// 在 Java 中,如果两个类在同一个包(也就是同一个目录)中,那么它们可以直接互相访问,无需使用 import 语句// 如上面的Main.java可以直接访问Helper.java,无需使用import导入// 但是,这个文件夹中的文件夹的文件是不可以直接访问的,仍需导入后才可以package com.myapp; import lib.mylib.MyLibrary // 不用写.javaimport com.myapp.util.Helper.* // 导入该文件夹下的所有类 public class Main { // ...}// 导入的区别 // C++#include <../../lib/mylib/sakura.h>// Javaimport lib.mylib.MyLibrary; /*java是直接从类路径开始寻找的,而c++是从当前路径开始寻找的*/javadoc 命令是用来生成自己写的 API 的帮助文档的
API 是 Application Programming Interface(应用程序编程接口)的缩写。它是一组预定义的规则和规范,允许一个软件应用(或组件)与另一个软件应用(或组件)进行交互和通信。
API 可以是函数、类、方法、数据结构等,以及它们的预期行为(例如输入、输出和副作用)。
相当于一个黑盒,只用知道输入,输出即可,中间的过程不需要知道
C++的 STL(Standard Template Library,标准模板库)也被视为一种 API,它是一组 C++模板类。
javadoc 是通过
/* * */上述的注释生成的,一般放在类或者方法的前面,用于解释它们
/* *@author 作者名 *@version 版本号 *@since 指明需要最早使用的jdk版本 *@param参数名 *@return 返回值情况 *@throws 异常抛出情况 */package base; /** * @author sakurakugu * @version 1.0 * @since 1.8 */public class Doc { String name; /** * @param name * @return name * @throws Exception */ public String test(String name) throws Exception { return name; }}生成 javaadoc 文档(网页)
# -d ../base/doc :用于改变生成路径为../base/doc# -encoding UTF-8 :用于指定源代码文件的字符编码位UTF-8# -charset UTF-8 :用于指定生成的 HTML文档的字符集为UTF-8javadoc -d ../base/doc -encoding UTF-8 -charset UTF-8 Doc.javaScanner 类用于获取用户的输入
// 基本语法Scanner s = new Scanner(System.in);new Scanner(System.in); // 输入这个,然后右键重构->引入变量通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串,在读取前我们一般需要使用 hasNext() 与 hasNextLine() 判断是否还有输入的数据。
Scanner scanner = new Scanner(System.in);String str = scanner.next(); System.out.println("使用next方式接收:");System.out.println(str); System.out.println("使用nextLine方式接收:");System.out.println(str); scanner.close();Scanner scanner = new Scanner(System.in); scanner.close();就是 scanf("%s")与 gets/getline 的在读取上面的区别
java 的 switch 语句相比于 c++,支持在 case 中用字符串作为检测值,其原理是通过将字符串转换为哈希值,然后再对比。
switch(name){ case "sakurakugu": System.out.println("英文");break; case "樱花莎": System.out.println("中文");break; ……}int[] a ={1,2,3};for(int i:a) System.out.println(i);for 循环也支持这种遍历,但没有 auto 语句
命名规范:是驼峰命名法,对于方法,还要首字母小写
总的来说,所有的方法都是函数,但并非所有的函数都是方法。如果一个函数是某个类或对象的一部分,那么它就是一个方法。
修饰符+c语言的函数的写法 // 修饰符可以没有// 如这个方法就是有 public static 两个修饰符,其创建的就是一个公共的静态的方法public static void main(String[] args) { }静态方法属于类,而实例方法属于类的实例
public class MyClass { public static void myStaticMethod() { System.out.println("This is a static method."); } public void myInstanceMethod() { System.out.println("This is an instance method."); }} // 第一个直接用 MyClass.myStaticMethod() 即可调用// 第二个要先创建一个类的实例 new MyClass().myInstanceMethod() 才可调用java 都是值传递(输入的格式都是值传递的格式),但对于基本数据类型,它是使用值传递。但对于对象、数组等,它是传递了一个指向对象的引用的副本(引用 ≈ 更安全的指针,但牺牲了一些低级别的控制)
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
方法的重载的规则:
实现理论:
public class MyClass { // 方法重载:同名方法,参数列表不同 public void display(String s) { System.out.println("One parameter: " + s); } public void display(String s, int i) { System.out.println("Two parameters: " + s + ", " + i); }}public class Main { public static void main(String[] args) { for (String arg : args) { System.out.println(arg); } }}java Main Hello World# java Main:运行Main类。其后面的是参数# 输出:HelloWorld注:如果有顶级包的话(以 “demo02” 中的路径为例),要在顶级包的路径中运行以下代码才行
Javapackage com.sakurakugu.method; public class demo02 { public static void main(String[] args) { for (String arg : args) { System.out.println(arg); } }}Bash# 要先切换到以下目录才可以编译java类cd "E:/elric/Programming/study/Java/self_study/JavaSE/Basic grammar/src"# 编译# 注:这里用的是路径,而非包名javac com/sakurakugu/method/demo02.java # 或者直接使用完整路径也可以javac "E:/elric/Programming/study/Java/self_study/JavaSE/Basic grammar/src/com/sakurakugu/method/demo02.java" # 运行# 注:这里用的是包名java com.sakurakugu.method.demo02 Hello World ! # 输出:HelloWorld!
可变参数(Varargs)是一种特性,允许你在调用函数时传入任意数量的参数。可以通过在参数类型后面添加三个点(...)来定义一个接受可变参数的函数。
可变参数必须是函数参数列表中的最后一个参数。如果一个函数有多个参数,那么可变参数必须放在最后
public class Main { public static void main(String[] args) { printNumbers(1, 2, 3, 4, 5); printNumbers(10, 20, 30); } public static void printNumbers(int... numbers) { for (int number : numbers) { System.out.println(number); } } // 如果有多个参数的情况 public static void printMessageAndNumbers(String message, int... numbers) { System.out.println(message); // 输出message for (int number : numbers) { System.out.println(number); } }}// 输出:12345102030 a11121314public static void printMessageAndNumbers(String message, int... numbers) { System.out.println(message); for (int number : numbers) { System.out.println(number); }}声明数组
数据类型[] 数组名; // 推荐使用这种方法 数据类型 数组名[]; // 这种是类C语法,但其都没有本质区别使用上面的那种方法是因为在构建函数时,可以将数组本身作为一种数据类型使用,而不用像 C 语言一样使用繁琐的指针
Javapublic static int[] reverse(int[] arrays){ int[] result = new int[arrays.length]; for(int i=0,j=result.length-1; i< arrays.length; i++,j--){ result[j] = arrays[i]; } return result;}
创建数组
数据类型[] 数组名 = new 数据类型[数组大小]; // 例:int[] arr2 = new int[5]; // 创建一个长度为5的整数数组int[] arr = {1, 2, 3, 4, 5}; // 创建一个包含指定元素的整数数组仅仅使用声明的方法是没有数组大小的,是无法那样创建的
// 可以使用 lenght方法来获取数组长度,(这不是C,没有括号,没有size() )for(int i=0;a.lenght;i++) { a[i]=i;}所有的对象都要使用
new创建像是
String str = "Hello";在内部等价于String str = new String("Hello");。但是,直接使用new String("Hello")是不推荐的,因为它会创建不必要的字符串对象。
在程序运行前就能确定数组的大小和数组中的初始数据我们称之为静态初始化。
float[] stuScores = {80.0f,70.0f,90.0f,66.5f};int[] nums = {80,70, 90,66};char[] chs = {‘a’,’b’, ‘c’,’d’};String[] stuNames = new String[]{"张三","李四","王五"}数组的大小或数组中数据在程序运行时才能决定,且用到new这个关键字来确定数组的大小或初始数据我们称之为动态初始化。 定义数组:变量类型[] 数组名 = new 数据类型 [ 数组长度 ];
int[] scores = new int[10];int[] stu = new int[size];定义数组与初始化分离:
int[] scores;scores = new int[lenght];数据类型[行][列] 数组名
静态:
int [][] scores={ {80,90,70}, {80,90,70}, {80,90,70}, {80,90,70},}动态:
// 法一int[][] scores = new int[2][3];// 法二int[][] scores;scores = new int[2][3];// 法三int[][] scores = new int[3][];scores[0] = new int[7];scores[1] = new int[5];// 行是三行排,之前把列与行的定义搞反了// 反正记住二维数组是先竖着多少个,然后横着多少个public static void main(String[] args) { int[][] a={ {1,2,3,4}, {3,4,5,5}, {67,3,9,6} }; for(int i=0;i<3;i++) { for(int j=0;j<4;j++) System.out.print(a[i][j]+" "); System.out.println(); }}// 输出:1 2 3 43 4 5 567 3 9 6Java内存
如下图,
array变量本身(即引用)存储在栈中,而它指向的数组对象(即数据)存储在堆中。这是 Java 中所有对象(包括数组)的通用行为

懒得写了,去看 JDK 文档即可
Object-Oriented Programming
面向过程思想 (如:打比赛)
面向对象思想(如:做项目)
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
**面向对象编程的本质就是:**以类的方式组织代码,以对象的组织(封装)数据。
**核心思想:**抽象
三大特性:
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象 (从好多人中抽象提取出教师、学生等)
从代码运行角度考虑是先有类后有对象。类是对象的模板。 (从学生这个抽象的概念中创造出学生小明,学生小红)(int 这个抽象的数据结构中创建出变量/容器 a,b,c 等)
/* 方法头: 修饰符 返回值类型 方法名(参数) { // 方法体 return 返回值; }*/-src -com -opp -demo01 Demo01.java Student.java// Student.javapackage com.oop.demo01; public class Student { // 静态的 public static void say(){ System.out.println("学生说话了啊啊啊"); } // 非静态的 public void say1(){ System.out.println("学生说话了?"); }}// 在Demo01中调用Student类的方法package com.oop.demo01; public class Demo01 { public static void main(String[] args) { // 可直接调用静态方法 Student.say(); // 对于非静态方法,需要先用new来实例化这个类 Student student = new Student(); student.say1(); student.say(); // √ (通过实例引用) Student.say(); // √ (静态引用/类引用) Student.say(); // × student.say1();// √ }}实例化一个类:
实例化一个类就是创建一个类的对象。在 Java 中,通过使用
new关键字来实例化一个类。当实例化一个类时,Java 会为该类的对象分配内存,并调用相应的构造函数来初始化对象。这个新创建的对象就是类的一个实例,它具有类定义的所有属性和方法。
例如,对于一个名为
Student的类,可以这样实例化它:Java// 对象类型 对象名 = 对象值Student student = new Student();在这个例子中,
new Student()创建了一个Student类的新实例,并将其引用赋值给student变量。现在,你可以使用student变量来访问Student类的属性和方法。
方法间的调用:
public class Demo01 { public static void a(){} public void b(){ a(); // √ } public static void c(){ a(); // √ } public void d(){ b(); // √ } public static void e(){ b(); // × }}因为 static 静态的是和类一起加载的,而非静态的是在类实例化后才存在的。
因此在未实例化之前,方法 b() 不存在,因此 方法 e() 是无法调用它的,所以不行,而其他的是可以的
值传递与引用传递
// cpp中的void add1(int a){} // 值传递void add2(int &a){} // 引用传递void add3(int *a){} // 指针传递// java中的package com.oop.demo01; public class Demo02 { public static void main(String[] args) { int a=1; System.out.println(a); change(a); System.out.println(a); Person person = new Person(); System.out.println(person.name); change(person); System.out.println(person.name); } public static void change(int a){ a=10; } public static void change(Person person){ person.name="Sakurakugu"; }} class Person{ String name;}// 输出:11nullsakurakugu在 Java 中,所有的方法参数传递都是值传递。这意味着当你将一个变量传递给一个方法时,实际上是传递的该变量的一个副本。
对于基本类型(如
int、double、char等),这个副本是变量的实际值。对于对象类型(如String、数组或任何其他对象),这个副本是对象的引用。当你传递一个
String给一个方法时,你实际上是传递了一个指向String对象的引用的副本。(本质还是值传递)这意味着方法可以使用这个引用来访问和修改String对象。然而,由于String是不可变的,所以任何对String的修改实际上都会创建一个新的String对象,而不会影响原始的String对象。所以,虽然在技术上,Java 对
String的传递是引用的值传递,但由于String的不可变性,它在实践中表现得更像是值传递。
类与对象的关系:
int a; 中,int 与 a 的关系)类与对象的创建:
main 方法
Application )中的 (如我之前的 C 语言控制台贪吃蛇游戏一样,这样也方便管理)new 创建对象时,会分配空间并进行默认的初始化,以及对类中构造器的调用学程序的作用也是对世界更好的建模。。。
之前学绘画的时候也是这么说的。。。
在 Java 中,构造器(Constructor)是一种特殊的方法,用于初始化新创建的对象。构造器的名称必须与类名完全相同,并且它没有返回类型。
当使用 new 关键字创建类的新实例时,Java 会自动调用相应的构造器来初始化新对象。我们可以为类定义多个构造器,以便在创建对象时提供不同的初始化选项。
以下是一个简单的 Student 类,其中包含两个构造器的例子:
public class Student { String name; int age; // 构造器 1:无参数 public Student() { this.name = "Unknown"; this.age = 0; } // 构造器 2:有参数 public Student(String name, int age) { this.name = name; this.age = age; }}在这个例子中,Student 类有两个构造器。一个是无参数的构造器,它将 name 设置为 "Unknown",将 age 设置为 0。另一个是有参数的构造器,它接受 name 和 age 作为参数,并将它们赋值给 Student 对象的属性。
你可以根据需要选择使用哪个构造器来创建 Student 对象:
Student s1 = new Student(); // 使用无参数构造器Student s2 = new Student("Alice", 20); // 使用有参数构造器但如果没有创建任何构造器,则在编译时 Java 会自己加一个空的构造器
// Person.java源文件package com.oop.demo02; public class Penson {} // 反编译得到的Person.class字节码文件package com.oop.demo02; public class Penson { public Penson() { }}如果只创建了一个含参构造器,但又用了无参的构造方法,就必须再创建一个无参的构造器,因为只要有了构造器,Java 在编译时就不会帮你形成一个构造器,所以会报错(这个特点与方法的重载有点像)
// 如上Student的类// 若使用下面两种方法构造,没有任何问题Student student = new Student();Student student1 = new Student("aa",1);// 但如上的构造器1没有创建的话Student student = new Student();// 这个方法就不可以使用,它会被指向到 构造器2 中一个类中若有多个构造器,这种情况被称为 构造器的重载
Alt+Ins (Insert) 可直接创造一个构造器总结:
构造器:
和类名相同
没有返回值
作用:
注意点:
定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造
this.是指向当前类的指针
在 Java 中,关键字"this"代表当前对象的引用。它可以用于引用当前类中的成员变量、成员方法和构造方法。
当一个对象调用自身的成员变量或成员方法时,可以使用"this"关键字来指明当前对象。例如,如果一个类中有一个成员变量叫做"age",那么可以使用"this.age"来引用当前对象的"age"变量。
此外,在构造方法中,"this"关键字可以用来调用当前类的其他构造方法。这种用法称为构造方法的重载。通过使用"this"关键字调用其他构造方法,可以避免代码的冗余。
Java// 指向构造方法时public class Point { int x; int y; Point(){ System.out.println("创建并初始化了⼀个Point对象"); } Point(int x,int y){ this(); this.x=x; this.y=y; } public double getDistance(){ return Math.sqrt(x*x+y*y); } public static void main(String[] args) { Point p = new Point(3, 4); System.out.println(p.getDistance()); }}总之,"this"关键字在 Java 中用于引用当前对象,帮助我们区分成员变量和局部变量,并且可以用于构造方法的重载。


** 类与对象的小结 **
类与对象
方法
引用
属性(字段 / 成员变量)
默认初始化值:
| 类型 | 默认值 |
|---|---|
| 数字 | 0 或 0.0 |
| char | \u0000 |
| boolean | false |
| 引用类型 | null |
语法格式:
修饰符 类型 属性名 = 初始值; // = 初始值 可选
对象的创建和使用
创建对象:
Person kuangshen = new Person(); // 必须使用 new 关键字 + 构造器使用对象:
kuangshen.name; // 访问属性kuangshen.sleep(); // 调用方法类的结构
面向对象三大特性
封装、继承、多态
该露的露,该藏的藏
封装(数据的隐藏)
记住这句话就够了:属性私有,get/set
"高内聚,低耦合"是软件设计的一种理想状态,是面向对象设计原则的核心。
"高内聚"是指一个模块或者一个类的功能应该是高度集中的,即该模块或类只做与其功能密切相关的事情。这样可以使模块或类的设计更清晰,也更容易理解和维护。
"低耦合"是指模块或类之间的依赖关系应该尽可能的少。如果模块或类之间的耦合度过高,那么一个模块或类的改变可能会影响到其他的模块或类,这将大大增加维护的难度和风险。
在
Student类中,say和say1方法都是关于"学生说话"的功能,这体现了高内聚。而这个类并没有依赖其他的类或模块,体现了低耦合。(self_study/JavaSE/Basic grammar/src/com/oop/demo02/Student.java)
属性私有,get/set
属性私有:就是可以设置一个属性是否可以直接被外面调用,还是不可以。
private : 私有
public : 公有
get/set(getter/setter)
就是设置两个公有的函数来调用私有的属性
public class Student{ // 私有的 不可使用 student.name 来调用 private String name; // 公有的 可使用 student.id 来调用 public String id; // get public String getName() { return name; } // set public void setName(String name) { this.name = name; } }可以通过 Alt+Ins 一键设置 get/set 函数
为什么要设置为属性为私有,并用 get/set 来改变/访问它呢
// 看这个例子就明白了 public class Student{ private int age; public int getAge() { return age; } public void setAge(int age) { if(age>120 || age<0) { System.out.println("年龄不符合规范,请重写"); } else { this.age = age; } } } /* 使用 student.getAge.sout + Tab 可快速变为 System.out.println(student.getAge()); */其作用:
点击某个方法,然后按住Ctrl可以快速查看其源码
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
如把“很多人”抽象成“人”,但通过继承,将“人”再次抽象为/分为“中国人”,“美国人”,“男人”,“女人”等等
// 学生 is a 人(“学生”继承“人”)public class Student extends Person{}extnds的意思是“扩展”。子类是父类的扩展。
Java 中类只有单继承,没有多继承!(C++/Python 中可以多继承)
Java 中的接口可以多继承((类)单继承,(接口)多实现)
// C++中的继承class Base1 { // Base1的成员定义}; class Base2 { // Base2的成员定义}; class Derived : public Base1, public Base2 { // Derived的成员定义};继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
子类和父类之间,从意义上讲应该具有“~ is a ~”的关系。
package com.oop.demo04; public class Person { public void say(){ System.out.println("我是人"); }}package com.oop.demo04; public class Student extends Person{ public void say(String s){ System.out.println("我是"+ s); }}package com.oop.demo04; public class Application { public static void main(String[] args) { Student student = new Student(); student.say(); student.say("sakurakugu"); }}// 输出:我是人我是sakurakugu在 Java 中,子类可以继承父类的以下内容:
注意,子类不能继承父类的构造器。每个类都有自己的构造器,不能被其他类继承。但是,子类可以通过 super 关键字来调用父类的构造器
按住
Ctrl+H可以查看其继承树
this关键字的作用和super关键字差不多,都时引用,只是一个是指向自身,一个是指向父类
object 类
在 Java 中,Object类是所有类的父类(老祖宗)。如果在类的声明中没有使用extends关键字明确指定一个父类,那么这个类就会默认继承Object类。
Object类位于java.lang包中,它定义了一些所有 Java 对象都共有的方法,如:
equals(Object obj):比较两个对象是否相等。hashCode():返回对象的哈希码值。toString():返回对象的字符串表示。getClass():返回对象的运行时类。在Student类中,虽然它直接继承的是Person类,但由于所有类都间接继承自Object类,所以Student类也可以使用Object类中定义的方法。
缺省类(默认类)
就是没有加 public、private 或 protected 成员访问符的类,其只能被当前包中的成员访问
super 关键字
在 Java 中,super关键字是一个特殊的引用,它引用的是当前对象的父类。super关键字主要有两种用途:
super关键字调用父类的构造器。例如,super()调用父类的无参数构造器,super(param)调用父类的带参数的构造器。super关键字调用父类的方法或访问父类的成员变量。package com.oop.demo04; public class Person { public Person(){ System.out.println("Person无参执行了"); } protected String name="sakurakugu"; public void print(){ System.out.println("Person"); }}package com.oop.demo04; public class Student extends Person{ public Student(){ // 默认调用了父类的无参构造器 super(); // 通过super()或this()调用构造器必须放在构造器的代码的第一行,因此两者只能有一个 System.out.println("student无参执行了"); } private String name="樱花莎"; public void test(String name){ System.out.println(name); System.out.println(this.name); System.out.println(super.name); } public void fun(){ print(); this.print(); super.print(); } public void print(){ System.out.println("Student"); }}package com.oop.demo04; public class Application { public static void main(String[] args) { Student student = new Student(); student.test("灵依"); student.fun(); }}// 输出:Person无参执行了 // 狗肇庆继承student无参执行了灵依 // 属性继承樱花莎sakurakuguStudent // 方法继承StudentPerson若子类与父类中有相同的方法,并且没有用
super或this关键字来选择,则会使用就近原则来选
方法重写
重写都是指方法的重写,和属性无关
方法重写(Override)是子类提供与父类方法签名完全相同的方法的机制。为什么需要重写:
方法重写的规则:
方法名、参数列表必须与父类方法完全相同,返回类型也必须相同或是父类方法返回类型的子类型。
访问修饰符的限制性不能比父类方法更严格。例如,如果父类方法是 public 的,那么子类重写的方法也必须是 public 的。public>Protected>Default>private
子类方法抛出的异常类型不能比父类方法更广泛。
还有:静态方法是类的方法,非静态是对象的方法
public class A extends B{ public static void test(){ System.out.println("A=>test()"); } public void test1(){ System.out.println("A=>test()"); }} public class B { public static void test(){ System.out.println("B=>test()"); } public void test1(){ System.out.println("B=>test()"); }} public class Application { //静态的方法和非静态的方法区别很大! //静态方法:方法的调用只和左边,定义的数据类型有关 //非静态:重写 public static void main(String[] args) { A a =new A(); B b =new A(); a.test(); b.test(); a.test(); b.test(); }}// 输出:A=>test()B=>test()A=>test()A=>test()可以通过
Alt+Ins一键重写
多态是指一个引用变量(如父类的引用)可以指向不同类型的对象(如子类的对象)。具体表现为“一种接口,多种实现”。
package com.oop.demo05; public class Person { public void run(){ System.out.println("run"); }}package com.oop.demo05; public class Student extends Person{ public void eat(){ System.out.println("eat"); }}package com.oop.demo05; public class Application { public static void main(String[] args) { // Student 能调用的方法都是自己的或者继承父类的! Student s1 = new Student(); // 父类的引用指向子类的对象 (因为:学生可以是人) // Person 父类型,可以指向子类,但是不能调用子类独有的方法 Person s2 = new Student(); // 同上理 Object s3 = new Student(); // 但是像下面 强制将猫转换成狗 就不可以了 String s4 =new Student(); // × // 这里就是继承的特性了,因为子类学生是继承于父类的,因此,当子类中找不到run()时,或没重写父方法时,往上找。 s2.run(); // 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大 // 编译看左,运行看右 s2.eat(); // × ((Student)s2).eat(); // √ 只要强制类型转换就可以了 }}在 Java 中,多态的实现主要依赖于两个机制:继承(inheritance)和接口(interface)。通过继承,我们可以创建具有共同属性和方法的类;通过接口,我们可以定义一组方法,然后由不同的类来实现这些方法。
多态存在的条件
注:多态是方法的多态,属性没有多态性
instanceof 关键字
用于检查一个对象是否是一个类的实例,或者是其子类或实现的接口的实例
强制类型转换
如上,当父类的引用指向子类的对象时,
可以将其对象进行强制转换
可以
老子有的,儿子都有;儿子有的,老子不一定有;老子要有儿子的,必须强制转换。
作用:方便方法的调用,减少重复的代码

静态方法与属性在此就不细写了
静态代码块
package com.oop.demo06; public class Student { // 输出以查看运行顺序 // 匿名代码块可以用于赋予初始值 { System.out.println("匿名代码块"); } static { System.out.println("静态代码块"); } public Student(){ System.out.println("构造方法"); } public static void main(String[] args) { Student student = new Student(); System.out.println("============"); Student student1 = new Student(); }} // 输出:静态代码块匿名代码块构造方法============匿名代码块构造方法可以发现,静态代码块只执行一次
静态导入包
静态导入包(static import)和直接导入包(regular import)是 Java 中导入包的两种方式,它们之间有一些区别:
- 直接导入包:使用
import关键字导入包时,需要在代码中指定具体类的路径或者整个包的路径。例如:import java.util.List;或者import java.util.*;。在代码中使用被导入的类时,需要加上类名或者使用完整的类路径。- 静态导入包:使用
import static关键字可以导入静态成员(如静态方法、静态变量)而不是整个类。这样在代码中就可以直接使用被导入的静态成员,而不需要使用类名来限定。例如:import static java.lang.Math.*;,这样就可以直接在代码中使用 Math 类中的静态方法,而不需要写Math.xxx。总的来说,静态导入包用于简化代码,使得可以直接使用静态成员而不需要限定类名,但是过度使用静态导入可能会使代码难以理解,所以应该适度使用。
package com.oop.demo06;// 静态导入包,如果不加static,则不能直接导入包中的静态成员import static java.lang.Math.random;import static java.lang.Math.PI; public class Test { public static void main(String[] args) { System.out.println(random()); System.out.println(PI); }}// 输出:0.388065667556167453.141592653589793在 Java 中,static是一个关键字,它表示一个成员(变量、方法、内部类)属于类本身,而不是类的实例。这意味着,无论我们创建了多少个类的实例,静态成员都只有一份。
以下是一些关于static关键字的重要点:
class MyClass { static int sharedVar = 10;}class MyClass { static void myMethod() { System.out.println("This is a static method."); }}调用静态方法:
MyClass.myMethod();class MyClass { static { // 静态块 }}class OuterClass { static class StaticNestedClass { // ... }}在 Java 中,final是一个关键字,它可以用于修饰类、方法和变量。以下是final关键字的主要用途:
final int MAX_VALUE = 10;Java 中,
const是一个保留关键字,但不能使用如果要创建一个常量,需要使用
final关键字
class MyClass { final void myMethod() { //... }}final class MyFinalClass { //...}final关键字主要用于实现不可变性和防止继承、重写。
在 Java 中,抽象类是一种不能被实例化的类(因此不能使用new关键字来创建对象),只能被继承。抽象类用于表示一种抽象概念,它可能包含一些抽象方法(没有实现的方法)(可类比为 C 语言中的只有函数声明)(只有抽象类才可以包括抽象方法)和非抽象方法(已经实现的方法)。抽象类是用abstract关键字声明的。
抽象类使用abstract关键字来声明。抽象方法也使用abstract关键字声明,但是它们没有方法体。
// 一个抽象类的例子abstract class Animal { //可以加pubilc等关键字的, abstract void makeSound(); // 这里也同理,只是懒得加 void eat() { System.out.println("The animal eats"); }}在这个例子中,Animal是一个抽象类,它有一个抽象方法makeSound()和一个非抽象方法eat()。子类必须提供makeSound()方法的实现,否则它们也必须被声明为抽象类。
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
当子类继承抽象类时,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
class Dog extends Animal { void makeSound() { System.out.println("The dog barks"); }}在这个例子中,Dog类继承了Animal类,并提供了makeSound()方法的实现。现在,我们可以创建Dog类的对象了。
Dog dog = new Dog(); // 创建Dog类的实例dog.makeSound(); // 调用实例的makeSound方法抽象类也可以存在构造器或抽象构造器
抽象类的作用就相当于目录/大纲了
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范!
自己无法写方法~专业的约束!约束和实现分离:面向接口编程~
接口定义了一个类所需要遵循的协议或契约,其他类可以通过实现这个接口来实现特定的行为。
接口是面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。
接口不是类,其中没有构造方法,因此不能实例化。
一个类可以实现一个或多个接口,从而继承接口中的抽象方法。
在接口中可以包含以下内容:
示例 1:
public interface Animal { void eat(); void sleep(); // 默认方法 default void breathe() { System.out.println("Breathing"); }}
breathe是一个默认方法,它在接口中有一个默认的实现。实现Animal接口的类可以选择覆盖这个方法,也可以选择不覆盖。如果不覆盖,那么就会使用接口中的默认实现。
然后,一个类可以实现这个接口,如下所示:
public class Dog implements Animal { @Override public void eat() { System.out.println("Dog is eating"); } @Override public void sleep() { System.out.println("Dog is sleeping"); } // 不覆盖breathe方法,使用接口中的默认实现}示例 2:
一般一个接口的实现类的取名是 接口名+Impl
package com.oop.demo08; public interface UserService { // 都是常量;(public static final int AGE = 99;) int AGE=99; // 接口中的所有定义的方法其实都是抽象的 public abstract void add(String name); void delete(String name); void update(String name); void query(String name);}package com.oop.demo08; public interface TimeService { void timer();}package com.oop.demo08; // 侧面实现了多继承public class UserServicelmpl implements UserService,TimeService{ @Override public void add(String name) {} @Override public void delete(String name) {} @Override public void update(String name) {} @Override public void query(String name) {} @Override public void timer() {}}接口的作用:
@Override
@Override 是 Java 中的一个注解,它告诉编译器以下的方法必须重写父类或者实现接口中的方法。如果没有在父类或接口中找到要重写的方法,编译器会报错。
这个注解不是必须的,但是使用它有两个主要的好处:
提高代码的可读性:@Override注解可以让其他开发者知道这个方法是重写了父类或接口的方法。
编译时检查:如果你打算重写父类或接口的方法,但是由于某些原因(例如方法签名的错误)并没有真正重写,那么编译器会给出错误提示。
例如:
public class Dog implements Animal { @Override public void eat() { System.out.println("Dog is eating"); } @Override public void sleep() { System.out.println("Dog is sleeping"); }}在这个例子中,Dog类实现了Animal接口,并且重写了eat和sleep方法,@Override注解就是用来标识这两个方法是重写了接口的方法。
内部类就是在一个类的内部在定义一个类,比如,A 类中定义一个 B 类,那么 B 类相对 A 类来说就称为内部类,而 A 类相对 B 类来说就是外部类了。
根据内部类的位置和特性,分为四种类型:成员内部类、静态内部类、局部内部类和匿名内部类
// 外部类package com.oop.demo09; public class Outer { private int id=10; public void out(){ System.out.println("这是外部类的方法"); } // 成员内部类 // 加一个 static 就是静态内部类 public class Inner{ public void in(){ System.out.println("这是内部类的方法"); } // 内部类中可以访问外部类中的私有属性 public int getID(){ return id; } } public void method(){ // 局部内部类 class Inner{ } } public static void main(String[] args) { // 匿名内部类 new Apple().eat(); }} // 一个Java类文件中,可以有多个class类,但只能有一个public class文件// 一般可用于临时的调试等等class Apple{ public void eat(){ System.out.println("救命呀,苹果吃人了!!!~"); }}成员内部类:定义在类的成员位置,和成员变量、成员方法的位置是一样的。它可以无条件访问外部类的所有成员,但是外部类想要访问内部类的成员,必须要创建内部类的对象。
public static void main(String[] args) { // 先实例化外部类 Outer outer = new Outer(); // 通过外部类来实例化内部类 Outer.Inner inner = outer.new Inner(); inner.in(); System.out.println(inner.getID());}静态内部类:定义在类的成员位置,但是它是用 static 修饰的。它的访问特性和静态成员方法是一样的,只能访问外部类的静态成员,不能访问非静态的成员。
局部内部类:定义在方法内、代码块内或者构造器内的类,它只能在定义这个内部类的方法内实例化并调用其方法,出了这个方法就无法访问。
匿名内部类:是局部内部类的一种,它没有名字,同时只能在定义的地方一次性的使用。
在 Java 编程语言中,异常是在程序执行期间发生的问题,它会打断正常的指令流。异常是由 Java 虚拟机(JVM)或者 Java API 抛出的,也可以由用户程序抛出。
就是那些标红呀,命令行中的报错呀等等
Exception:例外、异常
Java 异常分为两大类:
检查型异常(Checked Exceptions)
检查型异常是那些必须在编译时处理的异常,否则编译器会报错。
如:语法错误,打开不存在文件等等
运行时异常(Runtime Exceptions)。
运行时异常则是那些在程序运行时可能出现的异常,编译器不会强制要求处理这些异常。
如:下标越界、访问空指针、算术溢出、丢失资源等等
错误ERROR类:(不是异常Exception类)
Error类表示的是严重的问题,通常是程序无法自行处理的,如内存不足等,因此 JVM 一般会选择终止进程。在技术上也是很难处理的
如:内存不足/内存溢出、堆栈溢出、类定义错误、未知错误等
异常处理是 Java 中的一个重要概念,它允许程序员在发生错误时采取适当的行动,而不是让程序崩溃。Java 提供了 try-catch-finally 语句块来捕获和处理异常。
Java 把异常当作对象来处理,并定义一个基类 java.lang.Throwable 作为所有异常的超类。在 Java API 中已经定义了许多异常类,这些异常类分为两大类,错误 Error 和异常 Exception。
Java 的异常体系结构主要由Throwable类及其子类构成。
Throwable:这是所有错误和异常的超类。它有两个主要的子类
Error:这是当 Java 虚拟机(JVM)遇到严重问题时抛出的。例如,OutOfMemoryError和StackOverflowError。这些问题通常是严重的,程序无法恢复,因此,应用程序通常不会尝试捕获这些错误。Exception:这是应用程序可能希望捕获并处理的问题,或者可能希望通知用户存在的问题。
通常来说,
Exception是编写、测试时,大多都可以解决的。Error是难以在少量测试数据中发现并解决的
这就是 Java 的异常体系结构。在编写代码时,我们通常只需要关注和处理Exception类及其子类。

分为:
Java 的异常处理机制是一种用于处理运行时错误的强大机制。它使用了五个关键字:try、catch、finally、throw 和 throws。
try:try 块用于封装可能会抛出异常的代码。如果在 try 块中的代码抛出了异常,那么这个异常会被传递给与它匹配的 catch 块。
try { // 可能会抛出异常的代码}catch:catch 块用于处理 try 块中抛出的异常。每个 try 块可以跟随一个或多个 catch 块。
catch (ExceptionType1 ex) { // 处理ExceptionType1的代码}catch (ExceptionType2 ex) { // 处理ExceptionType2的代码}finally:无论是否发生异常,finally 块中的代码都会被执行。通常,finally 块用于清理代码,如关闭文件或数据库连接。
finally { // 清理代码}throw:我们可以使用 throw 关键字手动抛出一个异常,并结束程序。
throw new ExceptionType("异常信息");throws:如果一个方法可能会抛出异常,但并不处理它,我们可以在方法签名中使用 throws 关键字声明它可能会抛出的异常。
public void myMethod() throws ExceptionType1, ExceptionType2 { // 可能会抛出ExceptionType1或ExceptionType2的代码}如果一个方法可能会抛出一个可检查异常(即,这个异常是
Exception的子类,但不是RuntimeException的子类)(或者说叫自定义异常),那么你必须在方法签名中使用throws关键字声明这个异常。如果你没有这样做,那么编译器就会给出一个错误。
快捷键:
Ctrl+Alt+T用于将代码用语句包含起来
package com.exception; public class Demo01 { public static void main(String[] args) { int a=1; int b=0; try{ //try块中是监控区域 System.out.println(a/b); } catch(ArithmeticException e){ // 捕获异常 System.out.println("程序出现异常,变量b不能为0"); System.out.println(e.getMessage()); // 程序不会结束,会继续运行,若有finally,则先运行它 // 这里面也可以加一下处理异常的代码,如b++;等 } catch(Throwable e){ // 捕获异常 System.out.println("捕获到异常"); } // 可以没有finally finally { // 处理善后工作,如关闭IO流,资源等 System.out.println("finally"); } System.out.println("=========="); new Demo01().test(1,0); } public void test(int a,int b){ if(b==0){ // 主动抛出异常,并结束程序 throw new ArithmeticException(); } System.out.println(a/b); } // 在方法中抛出异常,如果出现以下两种异常之一,就抛出异常 public void test2(int a,int b) throws ArithmeticException,ClassCastException{ System.out.println(a/b); }}// 输出:程序出现异常,变量b不能为0/ by zerofinally==========Exception in thread "main" java.lang.ArithmeticException: / by zero at com.exception.Demo01.test2(Demo01.java:34) at com.exception.Demo01.main(Demo01.java:23)在 Java 中,可以通过继承 Exception 类(用于可检查异常)或 RuntimeException 类(用于运行时异常)来自定义异常。以下是一个自定义异常的基本步骤:
Exception 或 RuntimeException。String 参数的构造函数,这个构造函数应该调用父类的接受 String 参数的构造函数,这样你就可以提供一个关于这个异常的详细信息。以下是一个自定义异常的例子:
public class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); }}在这个例子中,MyException 是一个自定义的可检查异常。你可以像使用其他异常一样使用这个异常,例如,你可以使用 throw 关键字抛出这个异常,也可以在 catch 块中捕获这个异常。
例:
kage com.exception; public class Test { public static void main(String[] args) throws MyException { test(1); } static void test(int a) throws MyException { System.out.println("传递的参数为:"+a); if(a>10){ throw new MyException(a); } else{ System.out.println("OK"); } }}// 输出:传递的参数为:1OK//try块与上面自定义异常,优先输出自定义的异常try { test(1);} catch (MyException e) { System.out.println("MyException=>"+e);}// 输出:传递的参数为:1OKJavaWeb:网页编程(B/S 架构(浏览器)) ≠ 网络编程(TCP/IP)(C/S 架构(电脑))
核心目的:数据交换、相互通信
其核心概念包括:
网络协议(定义规则)
TCP
打电话:连接-接通-童话
UDP
发短信 发送就完事了-接受
HTTP、FTP 等
套接字(Socket)(建立连接)
客户端-服务器模型(进行通信)
异步编程(提高效率)
如何实现网络通信:
IP 地址的分类:
域名:记忆 IP 的问题
端口表示计算机上的一个程序的进程
不同的进程有不同的端口号,用来区分软件(不同进程的 PID 可以在任务管理器中查看)
规定端口分为: 0~65535 个(每种连接协议)
端口分类:
netstat -ano # 查看所有端口 netstat -ans|findstr "5900" # 查看指定的端口协议:约定,就好比我们箱子说的是普通话
网络通信协议: 速率,传输码率,代码结构,传输控制
作用:为了将复杂的通讯简化,更安全可靠的进行通信交换
TCP/IP 协议簇
包含:
互联网协议(IP)
IP 协议负责定义计算机在网络上的地址系统,即 IP 地址。它提供了数据报文的基本传送服务,通过路由选择算法将数据包从源地址传送到目的地址。
传输控制协议(TCP)
TCP 协议建立在 IP 协议之上。它负责将数据分割成小的数据块,并确保这些数据块按顺序到达目的地。TCP 还处理数据重传、流量控制和拥塞控制等问题,以保证可靠的数据传输。
辅助协议
用户数据报协议(UDP)
与 TCP 类似,UDP 也是运行在 IP 协议上的传输层协议,UDP 适用于需要快速传输但允许少量数据丢失的应用,如视频流和音频传输。
应用层协议
最少需要三次,以保证稳定连接客户端:我要连接。我叫A服务端:同意连接。 确认一下,我叫B,你叫A客户端:我也同意。再确认一下,我叫A,你叫B...开始连接...客户端:我想断开,我叫A服务端:同意。确认一下,你叫A,我叫B服务器:我想断开,我又叫BB客户端:同意。确认一下,你叫B,又叫BBpackage com.network.lesson01; import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket; // 服务端public class TcpServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket accept = null; InputStream is = null; ByteArrayOutputStream baos = null; // 这两者都是try中为核心代码 try{ // 1. 我要先有个地址 serverSocket = new ServerSocket(9999); // 2. 等待客户端链接过来 accept = serverSocket.accept(); // 3. 读取客户端的消息 is = accept.getInputStream(); // 管道流 baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { baos.write(buffer,0,len); } System.out.println(baos.toString()); } catch (IOException e){ e.printStackTrace(); } finally { // 关闭资源 if(baos!=null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if(is!=null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(accept!=null) { try { accept.close(); } catch (IOException e) { e.printStackTrace(); } } if(serverSocket!=null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }}package com.network.lesson01; import java.io.IOException;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;import java.nio.charset.StandardCharsets; // 客户端public class TcpClient { public static void main(String[] args) { Socket socket = null; OutputStream os =null; try { // 1. 要知道服务器的地址,端口号 InetAddress serverIP = InetAddress.getByName("127.0.0.1"); int port = 9999; // 2. 创建一个socket连接 socket = new Socket(serverIP,port); // 3. 发送消息 os =socket.getOutputStream(); os.write("你好,我是sakurakugu".getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { e.printStackTrace(); } finally { if(os!=null) { try { os.close(); } catch (IOException e){ e.printStackTrace(); } } if(socket!=null){ try{ socket.close(); } catch (IOException e){ e.printStackTrace(); } } } }}分享你的想法,与大家交流讨论
Twikoo 加载失败
Twikoo 脚本加载失败