下面的基础学习
, java进阶
, java高级
均来自菜鸟教程
java基础学习
组织结构method–class–file–package
java里的四个层次: method/property
👉 class
(object
)👉 .java
👉 package
- 每一个
后者
, 都能拥有多个前者
. - class在磁盘, 实例化后, object在内存.
- 一个
package
内所有、任何class都能互相访问.如果做了
import
, 那么当前类也能访问包外的类
了. - 一个
.java
文件只能有一个public class
, 其余的class都是private
的.一个文件也叫
编译单元
, 只能有一个公共接口
(用public类来表现).
强制规定public类名==文件名
, 因此在引用其它文件的类时无需显式声明。在编译时,编译器会根据类名去寻找同名文件。 - method中的特例, main方法:
public static void main(String[] args){...}
main是 Java 程序的入口点,只有作为应用程序的启动类才需要定义它.
因此, 它在后三个层次里都不是必须
的.
class
内部类
内部类可以随意使用外部类的成员变量(包括私有)而不用生成外部类的对象,这也是内部类的唯一优点
.
程序编译过后会产生两个 .class 文件,分别是 Out.class 和 Out$In.class.内部类对象
的生成: Out.In in = new Out().new In()
数据类型
分类
基本数据类型(primitive type)(仅8个):
- 原始类型:boolean,char,byte,short,int,long,float,double。
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double。
原始类型变成包装类型后, 就可以当
引用数据类型
使用了.引用数据类型(reference type): 剩下的都是.
如String
、数组
、类
、接口
、Lambda
表达式.
字符串类型
java的字符串相关的类有三种:
String
: 被 final 修饰的,他的长度是不可变的. 就算调用 String 的 concat 方法,那也是把字符串拼接起来并重新创建一个对象StringBuffer
: 长度可变的, 线程安全的,但是效率低. (基本没有适用场景)StringBuilder
: 长度可变的, 线程不安全, 但效率高. (最常用)
从 jdk1.5 开始,把所有用
加号连接的 string 运算
都隐式的改写成 stringbuilder,也就是说,用加号拼接字符串已经没有任何性能损失了。
但是,用循环拼接字符串
的时候,还是老老实实的用 stringbuilder 吧。
其他要点
- 自动类型转换(低级转高级):
byte, short, char
👉int
👉long
👉float
👉double
. - 不能对
boolean
类型进行类型转换。 - 不能把对象类型转换成
不相关类
的对象。 - 在把容量大的类型转换为容量小的类型时必须使用
强制类型转换
。 - 使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析.
字符串
- 其它类型👉 字符串的方法:
- 调用类的串转换方法:X.toString();
- 自动转换:X+””;
- 使用String的方法:String.valueOf(X);
- 字符串👉 其他类型的方法:
- 先转换成相应的封装器实例,再调用对应的方法转换成其它类型, 如
new Float("32.1").doubleValue()
或Double.valueOf("32.1").doubleValue()
- 静态parseXXX方法, 如
Double.parseDouble("32.1")
- 先转换成相应的封装器实例,再调用对应的方法转换成其它类型, 如
- 任何
字符类型
与字符串
相加,结果都是拼接。
变量
方法参数变量
的值传递方式有两种:
- 值传递:在方法调用时,传递的是实际参数的
值的副本
。所以参数变量的修改不会影响原始值。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 |
protected 需要从以下两个点来分析说明:
- 子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
- 子类与基类不在同一包中:那么在子类中,子类实例可以访问其
从基类继承而来的
protected 方法,而不能访问基类实例的
protected方法。
运算符
下表中, 上→下
的优先级高→低
.
类别 | 操作符 | 关联性 |
---|---|---|
后缀 | () [] . (点操作符) | 左到右 |
一元 | expr++ expr– | 从左到右 |
一元 | ++expr –expr + - ~ ! | 从右到左 |
乘性 | * /% | 左到右 |
加性 | + - | 左到右 |
移位 | >> >>> << | 左到右 |
关系 | > >= < <= | 左到右 |
相等 | == != | 左到右 |
按位与 | & | 左到右 |
按位异或 | ^ | 左到右 |
按位或 | | | 左到右 |
逻辑与 | && | 左到右 |
逻辑或 | | | | 左到右 |
条件 | ?: | 从右到左 |
赋值 | = + = - = * = / =%= >> = << =&= ^ = | = | 从右到左 |
逗号 | , | 左到右 |
数组
内置方法
数组的声明和创建(初始化):
double[] myList = new double[size]; myList[0] = 3.14; ...
double[] myList = {1.9, 2.9, 3.4, 3.5};
- 直接在函数参数里创建:
public void method1(new int[]{3, 1, 2, 6, 4, 2}) {...}
多维数组:
- 固定行列:
int[][] myArray = new int[2][3];
- 不固定:
int[][] myArray = new int[2][]; myArray[0] = new int[3]; myArray[1] = new int[2];
Array类
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态
的。
正则表达式
java的特殊语法: 两个 \\
代表其他语言中的一个 \
(转义字符)。
java.util.regex 包,它包含了 Pattern 和 Matcher 类,用于处理正则表达式的匹配操作。
|
异常处理
分类
- 检查性异常:在编译时由
编译器
强制要求处理的异常。如果一个方法可能抛出检查型异常,那么该方法必须声明抛出该异常,或者在其内部捕获并处理该异常。 - 运行时异常:在运行时由
JVM虚拟机自动抛出
的异常,编译器不要求强制处理这些异常。通常表示编程错误或逻辑错误。如数组下标越界 (ArrayIndexOutOfBoundsException)。
语法
- 一般地,
try
块后, 应至少有catch
或finally
块之一. - 而在
try-with-resources
语句中, 把资源声明
写在try后面, 可以确保在代码块结束时自动关闭资源(如文件、数据库连接等), 也就是隐式拥有了finally
的功能, 因此可以只有try
块.public static void main(String[] args) throws IOException{
try (Scanner scanner = new Scanner(new File("test.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
while (scanner.hasNext()) {
writer.print(scanner.nextLine()+ "\n");
}
}
}
java进阶–面向对象
继承
- java中只有
单继承
, 没有多继承
(多个父亲) - 关键字:
extends
: 继承父类, 如public class Penguin extends Animal{ ... }
.implements
: 类似多继承
, 仅限于子类继承多个父接口
. 如public class C implements A,B { ... }
.
- 构造器: 子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。
- 显式: 在子类构造器第一句写上:
super(参数列表)
. - 隐式: 如果子类构造器没有
super
, 则默认调用父类无参构造器.
- 显式: 在子类构造器第一句写上:
重写与重载
- 重写(Override): 外壳不变, 核心重写.
- 不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。
- 重载(Overload): 在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
多态
C++回顾:
虚函数
的概念用以支持多态
. 即在基类定义虚函数
, 在派生类中重写该函数, 然后通过基类指针或引用
调用虚函数时,实际调用的是派生类中重写的版本。
在Java中,
多态就是同一个接口,使用不同的实例而执行不同操作.
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象:
Parent p = new Child();
Java
普通函数
都相当于 C++ 的虚函数
,动态绑定是Java的默认行为。 如果想避免这种特性, final 关键字可变成非虚函数。Java的多态和C++类似: 子类重写了父类的函数, 那么在运行时, 即使子类对象的
引用变量的类型
是父类, JVM调用的仍然是子类重写后的函数.多态的实现方式:
- 重写
- 接口
- 抽象类和抽象方法
抽象类
抽象类是用abstract
修饰的类, 不能被实例化, 只能被继承. 抽象类中可以有抽象方法
, 也可以有非抽象方法
.
在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象方法: 抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。如public abstract double computePay();
声明抽象方法的后果:
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
另外, 构造方法
,类方法
(用 static 修饰)不能声明为抽象方法。
封装
封装(Encapsulation): 将抽象性函式接口的实现细节部分包装、隐藏起来。
步骤:
- 将类的属性私有化(private),
- 提供公共的(public)方法来获取(get)和设置(set)该属性的值。
接口
概念: 是一个抽象类型,是抽象方法的集合,以interface
来声明, 隐式抽象的, 因此不必写abstract
.
对比:
- 接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
- 接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是
抽象方法
(隐式默认为public abstract
, 因此不必写),且没有方法体.- Java 1.8 之后, 可以有方法体了, 但其声明要用
default
关键字修饰. - Java 1.9 之后, 可以有
private
方法.
- Java 1.8 之后, 可以有方法体了, 但其声明要用
- 接口可以有变量, 但均被隐式指定为
public static final
, 因此不必写。 - 接口不是被类继承了,而是要被类实现。
- 接口支持
多继承
(多个爸爸)。
- 接口与
抽象类
的区别- 接口中的方法不能有
方法体
- 接口中不能含有静态代码块以及静态方法(
static
)- Java 1.8 之后, 可以有了.
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
- 接口中的方法不能有
语法:
|
重写接口的方法时的注意事项:
- 类在实现接口的方法时,不能抛出
强制性异常
,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。 - 类在重写方法时要保持
一致的方法名
,并且应该保持相同或者相兼容的返回值类型。 - 如果实现接口的类是
抽象类
,那么就没必要实现该接口的方法。
标记接口:
标记接口是没有任何方法和属性的接口. 目的:
- 建立一个公共的父接口。
- 向一个类添加数据类型: 这个
接口名
就是所有派生对象被定义时的数据类型名
.
枚举
是特殊的类
, 一般用户只需定义一组常量. 这些常量默认为public static final
的.
语法:
|
枚举类的方法:
它默认继承了java.lang.Enum
类, 并实现了java.lang.Serializable
和java.lang.Comparable
两个接口. 因此有以下方法:
values()
: 返回枚举类中所有的值.valueOf()
: 将普通字符串转换成枚举实例.ordinal()
: 获取枚举变量在枚举类中的索引位置.compareTo()
: 比较两个枚举变量的顺序.toString()
: 返回枚举变量名.
包
包机制,用于区别类名的命名空间
。可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的
。
|
注意,使用通配符 * 导入整个包时,只会导入
包中的类
,而不会导入包中的子包
.
import 声明必须在包声明
之后,类声明
之前。
反射
反射(Reflection)是一个强大的特性,它允许程序在运行时
查询、访问和修改类、接口、字段和方法的信息。反射提供了一种动态地操作类
的能力,这在很多框架和库中被广泛使用,例如Spring框架的依赖注入。
反射 API
Java 的反射 API 提供了一系列的类和接口来操作 Class 对象。主要的类包括:
- java.lang.Class:表示类的对象。提供了方法来获取类的字段、方法、构造函数等。
- java.lang.reflect.Field:表示类的字段(属性)。提供了访问和修改字段的能力。
- java.lang.reflect.Method:表示类的方法。提供了调用方法的能力。
- java.lang.reflect.Constructor:表示类的构造函数。提供了创建对象的能力。
工作流程
获取类的
Class 对象
. ( 每个类在JVM中都有一个对应的 Class 对象.)// 方式1. 通过类字面量
Class<?> clazz = MyClass.class;
// 方式2. 通过对象的getClass()方法
MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();
// 方式3. 通过Class.forName()方法
Class<?> clazz = Class.forName("com.example.MyClass");获取成员信息:通过 Class 对象,可以获取类的字段、方法、构造函数等信息。
// 先获取构造函数, 创建新对象
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object clzObj = constructor.newInstance("John", 30);
// 获取字段
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // 如果字段是私有的,需要设置为可访问
Object value = field.get(clzObj); // 获取字段的值
// 获取方法
Method method = clazz.getMethod("methodName", String.class);
// 获取所有接口
Class<?>[] interfaces = clazz.getInterfaces();
// 获取父类
Class<?> superclass = clazz.getSuperclass();
3. 操作成员:通过反射 API 可以读取和修改字段的值、调用方法以及创建对象。
```java
// 修改字段
field.set(clzObj, "new value");
// 调用方法
method.invoke(clzObj, "arg1", "arg2");
// 调用构造函数
Object newObj = constructor.newInstance("arg1", "arg2");
## java高级
### 数据结构
在`java.util`包中, 有常用的如: 数组, 列表, 集合等.
```java
// 数组
int[] arr = new int[5];
// 列表--数组列表, 链表(双向)
List<String> list = new ArrayList<>();
List<String> list = new LinkedList<>();
// 集合--哈希表, 红黑树
Set<String> set = new HashSet<>();
Set<String> set = new TreeSet<>();
// 映射--哈希表, 红黑树
Map<String, Integer> map = new HashMap<>();
Map<String, Integer> map = new TreeMap<>();
// 栈
Stack<String> stack = new Stack<>();
// 队列
Queue<String> queue = new LinkedList<>();
// 堆
PriorityQueue<String> queue = new PriorityQueue<>();
PriorityQueue<String> queue = new PriorityQueue<>(Collections.reverseOrder());
// 树. 图. ...
java.util
中的其他数据结构:
- 枚举接口: 是传统接口, 已被迭代器取代.
- 位集合:
BitSet bits1 = new BitSet(16);
for(int i=0; i<16; i++) {
if ((i % 2) == 0) bits1.set(i);}
// bits1打印结果: {0, 2, 4, 6, 8, 10, 12, 14} - 向量: 向量(Vector)类和传统数组非常相似,但是Vector的大小能根据需要动态的变化。
- 字典: 弃用. 推荐使用 Map 接口及其实现类.
- 哈希表:
Hashtable
不再推荐, HashMap 和 ConcurrentHashMap 更常被推荐. - 属性:
Properties
类非常常用, 继承于Hashtable
. 表示一个持久的属性集. 常用于处理配置文件、属性文件和国际化资源时.Properties prop = new Properties();
// 加载配置文件
prop.load(new FileInputStream("config.properties"));
// 添加
prop.put("name", "John");
// 获取--单个属性值
String name = prop.getProperty("name");
// 获取--所有属性值
Set keys = prop.keySet();
Iterator it = keys.iterator();
while (it.hasNext()) {
String key = (String) it.next(); // print...
}
// 设置属性值
prop.setProperty("name", "John");
// 保存属性到文件
prop.store(new FileOutputStream("config.properties"), "Configuration");
集合框架
集合框架是用来代表和操纵集合
的统一架构。它包含了:
- 接口: 是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。
- 类: 是集合接口的具体实现。例如:ArrayList、LinkedList、HashSet、HashMap等。
- 算法: 是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序. 存在于
java.util.Collections
类中.
如排序算法:Collections.sort(mylist)
注意: 任何
没有使用泛型的对象
加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。
框架内的工具–迭代器
迭代器是一种设计模式,属于轻量级对象
.
- 作用: 可以逐个访问集合中的元素,而不需要使用传统的 for 循环或索引。这种方式更加
简洁和灵活
,并且适用于各种类型的集合。 - 使用:
next()
,hasNext()
,remove()
.import java.util.Iterator;
// 准备一个集合
List<String> list = new ArrayList<>(); ...;
// 创建迭代器
Iterator<String> it = list.iterator();
// 迭代器遍历
while (it.hasNext()) {
String str = it.next(); // 打印...
if(str.equals("abc")) {
it.remove(); // 删除
}
}
框架内的工具–比较器
比较器是用来定义对象的排序规则
的接口. 它包含两个方法:compare()
和 equals()
。
Object类
Object 是所有类的父类, 如果一个类声明时没有任何父类, 则默认继承了 Object 类.
泛型
- 泛型本质: 把
数据类型
参数化. - 类型参数: 所有泛型方法声明都有一个
类型参数
声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前
. - 类型参数的上限: (Upper Bound)即类型参数可以接受的类型范围, 用
extends
语法。如下面的例子2
- 泛型标记符:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ?- 表示不确定的 Java 类型, 即
类型通配符
泛型方法
例子1–允许数组的元素是任何数据类型:
|
例子2–定义一个泛型函数, 实现任意类型的对象作比较. 下面的<T extends Comparable<T>>
表示 T 必须是实现了 Comparable<T>
接口的类.
|
泛型类
|
java序列化
序列化
是一种将对象转换为字节流的过程,以便在网络上传输或存储在文件中。反序列化
则是将字节流转换回对象的过程。
如何让一个对象可序列化:
- 在定义它的类时必须实现java.io.Serializable 接口. 这个接口内没有任何方法, 需要被继承者实现.
- 该类的
所有属性
必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂
的。// 1.定义一个类, 它必须实现 java.io.Serializable 接口
import java.io.Serializable;
public class MyClass implements Serializable {
public transient int SSN; // 表面该属性不可被序列化
public int number;
public void mailCheck(){...}
}
// 2.序列化对象.
// 这里`ObjectOutputStream`是高层次的数据流,它们包含反序列化和序列化对象的方法。
MyClass obj = new MyClass();
try {
FileOutputStream fileOut = new FileOutputStream("object.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(obj);
out.close();
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
// 3.反序列化对象
MyClass obj = null;
try {
FileInputStream fileIn = new FileInputStream("object.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
obj = (MyClass) in.readObject(); //默认返回值为`Object`,因此需要将它转换成合适的数据类型。
in.close();
fileIn.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
java网络编程
两台机器利用套接字
建立TCP连接的步骤:
- 服务器实例化
ServerSocket 对象
,表示通过服务器上的端口通信。 - 服务器调用套接字对象的
accept() 方法
,它会一直等待直到客户端连接到服务器上给定的端口。 - 客户端实例化
Socket 对象
,指定服务器名称和端口号来请求连接
。 - 客户端的Socket 类的构造函数如果成功连接到指定的服务器和端口号,则创建
Socket 对象
与服务器通信。 - 服务器的
accept() 方法
等来了连接, 会返回一个新的 socket 引用
,该 socket 连接到客户端的 socket。 - 连接建立后,通过使用 I/O 流进行
双向通信
,即每一个socket都有一个输出流和一个输入流。
辨析两个Socket
类:
- ServerSocket: 服务器应用程序通过使用
java.net.ServerSocket
类以获取一个端口,并且侦听客户端请求。 - Socket:
java.net.Socket
类代表客户端和服务器都用来互相沟通的套接字。C/S两端都有一个Socket对象
, 其中,客户端
要获取一个 Socket 对象是通过类的实例化.服务器
要获取一个 Socket 对象是通过accept()
方法的返回值.
项目学习–前后端分离的博客系统
资料:
下面是自己全程学习的简要笔记
项目搭建
项目涉及前台
, 后台
两套系统, 为此在该项目(父模块
)中设计三个子模块: 公共子模块
(可被其他子模块复用), 前台子模块
, 后台子模块
.
在依赖管理方面:
- 父模块: 有
<dependencyManagement>
标签, 其中所有依赖均被锁定版本号
, 但不实际安装依赖. - 子模块:
<dependency>
中不指定版本号
.