以后的笔记按照这个图片的思路来做笔记:基础篇->进阶篇->高级篇->提高篇
一、基础篇:
1.基础篇
(1)面向对象:
2013年刚开始学C语言的时候,我们解决一个问题或者开发一个系统的时候都是按照这个系统需要哪些功能我们要编程哪些方法的思维来开发的,这种按照就是所谓的面向过程编程.
等到14年学习java编程前,记得有个计算机系主任来进行讲课《计算机导论》这节课,形象得讲说人类也是类(即java的class类),主任是人类的实例化,即实实在在的人类的对象,人类具有手、脚、眼睛等,这些就是类的成员变量(即属性),人类会跳舞、吃饭等行为属于成员方法(行为),也就是说我们看待现实世界客观事物的时候,不再以一种具备哪些功能的思路看待的时候(这种思路就是面向过程),而是这个对象具有哪些属性、哪些行为的时候,那就可以说,你具备了面向对象的思维了;
借此再说一下类和对象的关系:类就是对象的抽象,对象是类的实例化,例如说的人类就是一个抽象的概念,我们觉得具备有四肢、有头脑、有思维等特征的就是人类,主任就是实实在在的人,他就是人类的对象。
(2)三大特性
封装:封装就是想让成员方法和变量不让外界直接访问到,java用private进行私有化,类似我们生活中手机,提供了拨打电话的按钮,你不用去在乎这个打电话的功能是那些器件组成的,这个过程调制器,cpu,电话卡是怎么配合工作,都隐藏起来了,这就是封装的作用
说到private:提供一个图片
继承:我们有个汽车类,具有汽车架构、行驶功能,但是我们陆陆续续有定制化的小轿车也有汽车的架构、行驶功能,但是轿车还有自己独特的功能,这时候就可以继承汽车类,具备汽车的功能,对于自己其它的特征用途轿车自己添加,这样子可以实现代码复用
多态:同一个行为具有不同的表现形式就称为多态,例如画画这个行为,有的用彩色笔画画,有的用石墨画画,有的抽象派画画,有的现实派画画等等
下面经典的动物类多态例子,可以快速理解多态
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void work() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void work() { System.out.println("看家"); } }
(3)关键字:
关键字有50个,两个java团队还没有用到:goto 跳转到某个地方、const 常量(c语言也有这个常量关键词),其实还有3个特殊直接量:true、false、null,这里没有罗列进来
abstract | 表明类或者成员方法具有抽象属性 |
assert | 断言,用来进行程序调试 |
boolean | 基本数据类型之一,声明布尔类型的关键字 |
break | 提前跳出一个块 |
byte | 基本数据类型之一,字节类型 |
case | 用在switch语句之中,表示其中的一个分支 |
catch | 用在异常处理中,用来捕捉异常 |
char | 基本数据类型之一,字符类型 |
class | 声明一个类 |
const | 保留关键字,没有具体含义 |
continue | 回到一个块的开始处 |
default | 默认,例如,用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现 |
do | 用在do-while循环结构中 |
double | 基本数据类型之一,双精度浮点数类型 |
else | 用在条件语句中,表明当条件不成立时的分支 |
enum | 枚举 |
extends | 表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口 |
final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量 |
finally | 用于处理异常情况,用来声明一个基本肯定会被执行到的语句块 |
float | 基本数据类型之一,单精度浮点数类型 |
for | 一种循环结构的引导词 |
goto | 保留关键字,没有具体含义 |
if | 条件语句的引导词 |
implements | 表明一个类实现了给定的接口 |
import | 表明要访问指定的类或包 |
instanceof | 用来测试一个对象是否是指定类型的实例对象 |
int | 基本数据类型之一,整数类型 |
interface | 接口 |
long | 基本数据类型之一,长整数类型 |
native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 |
new | 用来创建新实例对象 |
package | 包 |
private | 一种访问控制方式:私用模式 |
protected | 一种访问控制方式:保护模式 |
public | 一种访问控制方式:共用模式 |
return | 从成员方法中返回数据 |
short | 基本数据类型之一,短整数类型 |
static | 表明具有静态属性 |
strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 |
super | 表明当前对象的父类型的引用或者父类型的构造方法 |
switch | 分支语句结构的引导词 |
synchronized | 表明一段代码需要同步执行 |
this | 指向当前实例对象的引用 |
throw | 抛出一个异常 |
throws | 声明在当前定义的成员方法中所有需要抛出的异常 |
transient | 声明不用序列化的成员域 |
try | 尝试一个可能抛出异常的程序块 |
void | 声明当前成员方法没有返回值 |
volatile | 表明两个或者多个变量必须同步地发生变化 |
while | 用在循环结构中 |
(3-1)用于数据类型。
用于数据类型的关键字有 boolean、byte、char、 double、 false、float、int、long、new、short、true、void、instanceof。
(3-2)用于语句。
用于语句的关键字有break、case、 catch、 continue、 default 、do、 else、 for、 if、return、switch、try、 while、 finally、 throw、this、 super。
(3-3)用于修饰
用于修饰的关键字有 abstract、final、native、private、 protected、public、static、synchronized、
transient、 volatile。
(3-4)用于方法、类、接口、包和异常。
用于方法、类、接口、包和异常的关键字有 class、 extends、 implements、interface、 package、import、throws。
还有些关键字,如cat、 future、 generic、innerr、 operator、 outer、rest、var等都是Java保留的没有意义的关键字。
另外,Java还有3个保留字:true、false、null。它们不是关键字,而是文字。包含Java定义的值。和关键字一样,它们也不可以作为标识符使用。
(4)基本数据类型:
整数
4-1) byte Byte
byte是一个8位数的整数型,继承Nunber并且实现Compareable
extends Number implements Comparable<Byte>
最大值最小值
public static final byte MIN_VALUE = -128;
/**
* A constant holding the maximum value a {@code byte} can
* have, 2<sup>7</sup>-1.
*/
public static final byte MAX_VALUE = 127;
compareTo方法返回 x-y
public static int compare(byte x, byte y) {
return x - y;
}
hashcode返回对应的int值
public static int hashCode(byte value) {
return (int)value;
}
等等
4-2)short
继承实现同上
由于是2字节16位,最小值-1*2的15次方 最大值 2的-15次方-1
public static final short MIN_VALUE = -32768;
public static final short MAX_VALUE = 32767;
hashcode一样返回int
public static int hashCode(short value) {
return (int)value;
}
compare 一样
4-3)int
类似的
4-4)long
类似的
浮点型
4-5)float
每次都返回新的对象
public static Float valueOf(float f) {
return new Float(f);
}
4-6)double
每次都返回新的对象
public static Double valueOf(double d) {
return new Double(d);
}
字符型
4-7)char
这个也是有缓存
static final Character cache[] = new Character[127 + 1];
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
布尔型
4-8)bool
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
(5)String
5-1)为了写这个总结,特意去java的官网https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/String.html看了一下String描述:
The String
class represents character strings. All string literals in Java programs, such as "abc"
, are implemented as instances of this class.
Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared. For example:
String str = "abc";
大概讲了就是String类属于不可变的,看源码可以知道 public final class String 用了final修饰
5-2)除此之外,String都是放在方法区,有一个字符串常量池维护着,所以我们每次创建字符串都会去查看常量池看看是否已经存在,若已经存在直接引用
5-3)字符串比较,比较是属于ascii,例如
String a="1";
String b="a";
System.out.print(a.compareTo(b));
打印-48 因为1对应49,a对应97,相减为-48
5-4)常用的几个String方法
字符串下标截取
String b="123456";
System.out.println(b.substring(2,4));
打印34
int等基础类型转换成字符串
int n=123456;
String c=String.valueOf(n);
5-5)比较StringBuffer,StringBuilder
(6)集合
这个有点多,具体参照下图
具体参照:https://blog.csdn.net/qingwengang/article/details/80394957
由于篇幅太多,我们就讲几个
Collection下面的List:Vector(线程安全)LinkedList/ArrayList
6-1) ArrayList 继承->AbstractList 继承->AbstractCollection具体看图
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable 说一下核心: public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable { // 序列化id private static final long serialVersionUID = 8683452581122892189L; // 默认初始的容量 private static final int DEFAULT_CAPACITY = 10; // 一个空对象 private static final Object[] EMPTY_ELEMENTDATA = new Object[0]; // 一个空对象,如果使用默认构造函数创建,则默认对象内容默认是该值 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0]; // 当前数据对象存放地方,当前对象不参与序列化 transient Object[] elementData; // 当前数组长度 private int size; // 数组最大长度 private static final int MAX_ARRAY_SIZE = 2147483639; // 省略方法。。 }
然后看看三个构造方法
/** * Constructs an empty list with an initial capacity of ten.我们没有传变量的时候设置为空,此时还没有开辟新的内存,直接指向类成员DEFAULTCAPACITY_EMPTY_ELEMENTDATA */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //根据传过来的容量来,大于0初始化,等于0,用到EMPTY_ELEMENTDATA,小于0报错 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA;//这个1.7版本是会直接 this.elementData = new Object[initialCapacity]; 这里避免了new 空对象 } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } //这个构造方法直接可以传Collection过来,Arrays.copyOf是深拷贝,相当于elementData用了新的地址了 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
看到这里,其实有人还是没太大明白,为什么要声明EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA呢?
其实在add方法,每次add,都要保证不会下标溢出,所以有了ensureCapacityInternal方法
步骤1 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! 这里是保证容量的方法,调用calculateCapacity elementData[size++] = e; return true; } 步骤2 private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } 步骤3 //计算容量的 private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity);//如果你刚开始没有赋值,那我就给你默认值DEFAULT_CAPACITY 10 } return minCapacity; } 步骤4 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 步骤5 真正在扩容 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);//这里就是经常说的扩容1.5倍 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
参照:
https://blog.csdn.net/weixin_43390562/article/details/101236833
https://blog.csdn.net/augfun/article/details/82323164
6-2)LinkedList
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable 看一下主要有两个 /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ //学过C就知道是指针的头部,这里是指第一个对象 顺便提一下transient 是不进行序列化 transient Node<E> first; /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ //最后一个 transient Node<E> last; //内部类 private static class Node<E> { E item;//正在的对象,存放数据 Node<E> next;//下一个node地址 Node<E> prev;//前面一个node地址 Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } public boolean add(E e) { linkLast(e);//默认添加后末端 return true; } void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null)//如果为空,说明加的第一个 first = newNode; else l.next = newNode;//下一个指向新add的node size++; modCount++; } //向某个下标插入E public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index));//node方法根据index遍历获取node对象 } //主要看linkBefore,下面画了个图,大概可以描述清楚 void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode;// if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
、
我做了个实验,分别给LinkedList和ArrayList添加100000和打印100000,添加效率差不多,打印的时候,Link就慢得多
Arr:4297
Link:46282
6-3)ArrayList
ArrayList和Vector都是使用数组方式存储数据,其余区别不大
这个加了同步锁,例如
public synchronized void ensureCapacity(int minCapacity) { if (minCapacity > 0) { modCount++; ensureCapacityHelper(minCapacity); } }
Vector:4166
还有Map下面的:HashMap、Hashtable、ConcurrentMap
6-4)HashMap主要为散列表:
table为数组,然后每个Node为链表,简称散列表 transient Node<K,V>[] table; 添加时候主要调用 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null)//数组的数量-1,和hash做与运算,初始值为16,16-1为15,二进制位1111,这种情况的哈希冲突是最少的 tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
6-5)HashTable主要是加了synchronized
6-6)ConcurrentMap这个在1.7使用segment分段锁,好处就是改进了Hashtable的锁住整个table,坏处就是要每次查找2次hash
<7>枚举
枚举就两种写法
public enum TestEnum { RED(1), GREEN(2), BLANK(3), YELLOW(41); int testStatus; TestEnum(int i) {//一定要构造方法 testStatus=i; } public int getTestStatus() { return testStatus; } public void setTestStatus(int testStatus) { this.testStatus = testStatus; } }
public static void main(String[] args) {结果
System.out.println(TestEnum.YELLOW.testStatus);//这里有点类似于static的使用
}
Connected to the target VM, address: '127.0.0.1:57115', transport: 'socket'
41
Disconnected from the target VM, address: '127.0.0.1:57115', transport: 'socket'
写法2
public enum UseStatusEnum { PACKING_NOT_USE(1,"11"), // 未领用 PACKING_HAS_USED(2,"22"), // 已领用 PACKING_BACK_USE(3,"33"); // 已打回 private Integer useStatus; private String str; private UseStatusEnum(int useStatus,String str) { this.useStatus = useStatus; this.str=str; CollectionUtils.isEmpty(new HashMap<>()); } public Integer getUseStatus() { return useStatus; } public String getStr() { return str; } public static void main(String[] args) { System.out.println(UseStatusEnum.PACKING_NOT_USE.getStr()); } } 结果 Disconnected from the target VM, address: '127.0.0.1:57131', transport: 'socket' 11 Process finished with exit code 0
(8)包装类
查看基础数据类型
(9)反射
我以前文章
https://www.cnblogs.com/imfjj/p/8075496.html
(10)动态代理
https://www.cnblogs.com/imfjj/p/11603135.html
(11)序列化
public class MySerial implements Serializable { private static final long serialVersionUID = 1L; String name="imfjj"; byte age=18; public String getName() { return name; } public void setName(String name) { this.name = name; } public byte getAge() { return age; } public void setAge(byte age) { this.age = age; } public static void main(String[] args) { try { ObjectOutputStream o=new ObjectOutputStream(new FileOutputStream("mySerial.out")); MySerial mySerial=new MySerial(); o.writeObject(mySerial); o.flush(); o.close(); } catch (IOException e) { e.printStackTrace(); } deserialize(); } private static void deserialize(){ ObjectInputStream objectInputStream = null; try { objectInputStream =new ObjectInputStream(new FileInputStream("mySerial.out")); } catch (IOException e) { e.printStackTrace(); } try { MySerial mySerial = (MySerial) objectInputStream.readObject(); System.out.println(mySerial.getAge()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
结果:
Connected to the target VM, address: '127.0.0.1:58620', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:58620', transport: 'socket'
18
Process finished with exit code 0
假如改动
byte age=18;改动为
transient byte age=18;
输出则为 0;
(12)注解
https://www.cnblogs.com/imfjj/p/9890061.html
(13)泛型
参照:概念性的描述https://www.cnblogs.com/wyb666/p/10349178.html
<T>T 和T 的区别https://www.cnblogs.com/jpfss/p/9929108.html
https://blog.csdn.net/weixin_52772307/article/details/126868855
补充:T相对于<T>T是调用前就限制了类型了,而<T>T还没有限制死,调用的时候用哪个类型,就返回哪个T
(14)SPI
SPI就是java设计了
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。
参考:
https://www.cnblogs.com/jy107600/p/11464985.html
https://blog.csdn.net/codingtu/article/details/79004657
(15)异常
一种可捕获Exception,错误为Error,例如StackOverFlow OMM
(16)IO流
分为字节流和字符流,具体看我以往文章
https://www.cnblogs.com/imfjj/p/10972199.html
(17)补充反编译
字符串反编译命令:写了一个Test.java
public class Test { public static void main(String[] args) { String a="hello"; } }
使用指令 javac Test.java & javap -c Test
public static void main(java.lang.String[]);
Code:
0: ldc #2; //String hello
2: astore_1
3: return
}
idc代表存进字符串常量池,hello写进去了