(_a1,$a, 1a(错))
1)strictfp(精确浮点类型)
内部所有的float和double都是精确浮点类型
transient(不可序列化)
标识变量不可被序列化
synchronized(方法锁)
在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
volatile(强迫从共享内存中重读该成员变量的值)
volatile修饰变量。在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java volatile关键字最全总结:原理剖析与实例讲解(简单易懂)_夏日清风-CSDN博客
Java并发编程:volatile关键字解析 - Matrix海子 - 博客园 (cnblogs.com)
https://www.cnblogs.com/dolphin0520/p/3920373.html
Native(其他语言实现方法)
native是方法修饰符。Native方法是由另外一种语言(如c/c++,FORTRAN,汇编)实现的本地方法
String:少量数据
StringBuffer:线程安全
StringBuilder:线程不安全、性能好
StringBuffer的方法增加了synchronized关键词修饰,线程安全。
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
Byte、Short、Integer、Long、Character、Float、Double、Boolean
自动、手动
clone(), finalize()……
out, in, err
exit()、gc()
binarySearch(先排序)
sort(倒排Collections.reverseOrder())
parallelPrefix(修改值,两参数前元素和当前元素)
修改元素值,匿名类重写方法两个参数
parallelSetAll(赋值,参数为索引号)
设置元素值,匿名重写方法一个参数
parallelSort(并行排序)
int[] ints = new int[3];
int[] newInts1 = Arrays.copyOf(ints, 3);
int[] newInts2 = Arrays.copyOfRange(ints, 0, 2);
int[] newInts3 = new int[2];
System.arraycopy(ints, 0, newInts3, 0, 2);
int[] newInts4 = ints.clone();
继承(单继承、多借口)
单继承避免冲突,多接口
封装(复杂性)
类的用途就是封装复杂性
多态(继承、重写、向上转型)
多态三必要条件:继承、重写、父类引用指向子类对像【向上转型】
class Animal {
public void Eat() { System.out.println("eat food"); }
}
class Cat extends Animal { // 继承
public void Eat() { System.out.println("eat fish"); } // 重写
}
Animal animal = new Cat(); // 向上转型
animal.Eat();
//输出 eat fish
显示创建(new、反射、clone、readObject)
反射:className.newInstance();
clone()
java.io.ObjectlnputStream 对象的 readObject() 方法
隐式创建(String、Class实例)
String str = “abc”;
String str2 = “123” + str;
java虚拟机加载一个类时会自动创建其Class实例
分配内存
将变量初始化为默认值
给变量赋初始值
回收条件(引用、null)
对象引用超过其作用范围
对象被赋值null
三种状态(触及、复活)
可触及、可复活、不可触及
只在堆内存中开辟空间
没有栈内存的引用
new Person(“Li”).tell();
控制:public、friendly(同包)、protected(同包、子类)、private
static:
final:类不可继承、方法不可重写、变量不可修改
this 指的是当前对象的引用,super 是当前对象的父对象的引用。
this 和 super 不能同时出现在一个构造方法里面
this( ) 和 super( ) 都指的是对象,所以,均不可以在 static 环境中使用
从本质上讲,this 是一个指向对象本身的指针, 然而 super 是一个 Java 关键字
向上转型
父亲引用指向子类对象。
FatherClass fc = new SonClass();
向下转型
子类引用指向父亲对象。需要强制转换。
SonClass sc = (SonClass)fatherClass;
如果父亲对象不是子类的实现,那么这个强制转换会运行时报错。
可以加判断 instancof SonClass
重载:多个方法名称相同,具有不同的参数(形参列表不同),返回类型可以相同也可以不同
重写:子类覆盖父类的方法,加@Override注解
注意:
final、static方法不可以被重写;
如果不能继承一个方法,则不能重写
在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为。
分为编译时多态和运行时多态。
3 个必要条件:继承、重写和向上转型。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。
判断一个对象是否为一个类(或接口、抽象类、父类)的实例
boolean result = obj instanceof Class
抽象类的定义和使用规则如下:
abstract 关键字声明。
如果一个方法被声明为抽象的,那么这个类也必须声明为抽象的。
一个抽象类中,可以有 0~n 个抽象方法,以及 0~n 个具体方法。
抽象类不能实例化,也就是不能使用 new 关键字创建对象。
抽象方法的 3 个特征
抽象方法没有方法体
抽象方法必须存在于抽象类中
子类重写父类时,必须重写父类所有的抽象方法
接口声明public和private不同,public包外可用,private仅包内
方法,隐式地声明为公有的(public)和抽象的(abstract)
变量,隐式地声明位public,static,final,即常量,必须初始化
没有构造函数
外部类
编译之后有独立的.class文件,但是前面增加了外部类名称和$
外部类以外的类访问内部类
OutClass.InnerClass ic = new OutClass().new InnerClass();
静态内部类
访问控制参考静态方法
局部内部类
方法内定义的类
不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰
只可以访问当前方法中 final 类型的参数与变量
重名的话
匿名内部类
需要在外部定义一个类、抽象类或接口;
外部类的方法内部可以new这个类(接口),并重新定义方法
可以有非静态代码块,在父类的构造函数后执行
内部类实现多重继承
抽象类Sing,方法singSong(),抽象类Dance,方法dangceDance()
类Actor extends Sing,定义匿名内部类
Dangce d = new Dance(){
//重写方法danceDance()
}
调用,a.singSong(); a.d.danceDance();
effectively final
Java8开始支持的新特性;
局部内部类,或匿名内部类访问的变量必须是final,如果没有添加的话,编译器会自动将其设置为effectively final,而不需要手动修改位final,但是如果其他地方有对变量修改的话会报错
访问控制
定义函数式接口,@FunctionalInterface注解,只能有一个抽象方法,但可以添加默认方法和静态方法;
在使用的地方用Lambda实现接口
// 可计算接口 public interface Calculable { // 计算两个int数值 int calculateInt(int a, int b); } |
Calculable cal = (int a, int b) -> { // 访问静态成员变量,不能访问实例成员变量 staticValue++; int c = a + b + staticValue; // this.value; return c; }; Int rst = cal(3, 5); |
Lambda 表达式只能访问局部变量而不能修改,否则会发生编译错误,但对静态变量和成员变量可读可写。
定义方法,方法参数返回和接口方法一致
静态方法:DemoClass::add,普通方法:new DemoClass():sub
public class LambdaDemo { // 静态方法,进行加法运算 // 参数列表要与函数式接口方法calculateInt(int a, int b)兼容 public static int add(int a, int b) { return a + b; }
// 实例方法,进行减法运算 // 参数列表要与函数式接口方法calculateInt(int a, int b)兼容 public int sub(int a, int b) { return a - b; } } |
// 打印加法计算结果,静态方法 display(LambdaDemo::add, n1, n2); LambdaDemo d = new LambdaDemo(); // 打印减法计算结果 ,普通方法 display(d::sub, n1, n2);
public static void display(Calculable calc, int n1, int n2) { System.out.println(calc.calculateInt(n1, n2)); }
|
C#需要定义delegate
pubulic delegate int CalAdd(int a, int b);
使用的出显示声明delegate类型
CalAdd add = (int a, int b) => {return a+b};
两者的定义方式不同,C#定义delegate即可,Java没有delegate,需要定义接口并且需要@FunctionInterface注解。
finally中的代码总会被执行,除非在try和catch中调用了system.exit()。
当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响。
finally中有return时,会直接在finally中退出,导致try、catch中的return失效。
Java7之前
手动在finally里释放资源,且需要再增加try catch语句
public static void main(String[] args) { FileInputStream fis = null; try { fis = new FileInputStream("a.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { // 关闭磁盘文件,回收资源 if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
Java7
try ( // 声明、初始化两个可关闭的资源 // try语句会自动关闭这两个资源 BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java")); PrintStream ps = new PrintStream(new FileOutputStream("a.txt"))) { // 使用两个资源 System.out.println(br.readLine()); ps.println("C语言中文网"); } |
Java9
// 有final修饰的资源 final BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java")); // 没有显式使用final修饰,但只要不对该变量重新赋值,该变量就是有效的 final PrintStream ps = new PrintStream(new FileOutputStream("a. txt")); // 只要将两个资源放在try后的圆括号内即可 try (br; ps) { // 使用两个资源 System.out.println(br.readLine()); ps.println("C语言中文网"); } |
和C#对比
using (var fs = File.Open("xxxx", FileMode.Open)) { // } |
throws和throw:声明和抛出异常
throws声明方法可能抛出的所有异常信息,表示一种可能性,但不确定;throw则确定抛出的异常类型
方法或类处,可以通过throws声明;方法内部通过throw声明
throws由系统自动捕获异常;throw需要用户自己捕获异常,并对异常处理
子类重写父类方法,throws的异常需要是父类方法throws异常的子类或相同。
4 个目标
成功的异常处理应该实现如下 4 个目标
使程序代码混乱最小化。
捕获并保留诊断信息。
通知合适的人员。
采用合适的方式结束异常活动。
基本准则
不要过度使用异常
不要使用过于庞大的try块
避免使用 Catch All 语句
不要忽略捕获到的异常
Java集合类型分为Collection和Map,是两个根接口。
Queue是队列的实现,Dueue在其基础上实现了双向队列;
List是个可以精准控制每个元素的插入位置;
Set不允许重复,Map存放Key-Value键值对。
HashSet:底层用HashMap实现,优化了查询性能,不保证顺序;
TreeSet:有序Set,内部用TreeMap实现,;
ArrayList:用数组实现,数组可变大小,可以快速随机访问元素;
核心代码:
elementData = Arrays.copyOf(elementData, newCapacity);
LinkedList:对顺序访问进行了优化,但随机访问性能较慢,一般作为Stack和Queue使用;底层使用了Node内部静态私有类,该类有next和prev字段。
HashMap:哈希算法来存取键对象;
TreeMap:可以对键对象排序,put的时候就排好序了。
ArrayList
构造方法
ArrayList():初始化容量10的空列表
ArrayList(Collection extends E> c):按照c的迭代器返回的顺序添加
增加了根据索引获取和修改元素的方法,以及根据元素获取索引的方法
ArrayList 类实现了可变数组的大小
核心代码:
elementData = Arrays.copyOf(elementData, newCapacity);
LinkedList
采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素
private static class Node
E item;
Node
Node
Node(Node
this.item = element;
this.next = next;
this.prev = prev;
}
}
频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高
随机访问元素的速度则相对较慢
只对首末元素操作便捷,可用于Stack和Queue
HashSet
底层使用HashMap,根据Hash算法作为Key,存取和查找性能很好
添加相同的元素,后添加的会覆盖前面的,不重复
TreeSet
实现对集合进行自然排序,只能对实现了 Comparable 接口的类对象进行排序
底层用TreeMap实现
HashMap
数据存放
transient Node
Node类型定义
static class Node
final int hash;
final K key;
V value;
Node
其中hash的计算方法:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
TreeMap
可以对键对象排序。
遍历
foreach
for (Map.Entry
迭代器
Iterator
keySet()和valueSet()迭代
代码
Iterator it = objs.iterator();
while (it.hasNext()) {
String obj = (String) it.next();
}
快速失败(fail-fast)机制,是 Java Collection 集合中的一种错误检测机制。
只有通过 Iterator 的 remove() 方法删除上一次 next() 方法返回的集合元素才可以
操作 Set、List 和 Map 等集合的工具类
排序:sort()
最大、最小值:max(), min()
二分查找:binarySearch(),先排序再查找
反转顺序:reverse,reverseOrder(反转比较器)
将集合变为线程安全:synchronizedCollection,synchronizedList……
和Arrays比较,Arrays是操作数组的类
Arrays.binarySearch//二分查找
Arrays.copyOf //复制
Arrays.copyOfRange//复制部分
Arrays.sort//排序
Arrays.fill//填充
Arrays.toString//字符串返回
Arrays.hashCode//哈希值
Arrays.asList//将数组转为List
forEach
objs.forEach(m-> System.out.println(m));
removeIf
objs.removeIf(ele -> ((String) ele).length() < 12);
Java 8 还新增了 Stream、IntStream、LongStream、DoubleStream 等流式 API
用法1
IntStream is = IntStream.builder().add(20).add(13).add(-2).add(18).build();
用法2
objs.stream().filter(ele -> ((String) ele).length() > 12).count()
中间方法
filter, mapToxxx, peek, distinct, sorted, limit
末端方法
forEach, toArray, reduce, min, max, count, findFirst, findAny, anyMatch, allMatch, noeMatch
Set/List
Set.of,List.of,
Set set = Set.of("Java", "Kotlin", "Go", "Swift");
Map
Map map = Map.of("语文", 89, "数学", 82, "英语", 92);
Map map2 = Map.ofEntries(Map.entry("语文", 89), Map.entry("数学", 82), Map.entry("英语", 92));
仅是给编译器使用的,编译后会对泛型进行擦除。
证明:
先声明一个ArrayList
然后list.Add(1)
使用反射为list添加一个字符串,可以正常添加
list.getClass().getMethod("add", Object.class).invoke(list, "asd");
并且编译后读取数据可以正确读出不报错。
C#是真泛型,在第四步读取时会报错;
C#为每个泛型都生成了一个新的类型,为了考虑性能,CLR只会为相同的泛型组合编译一次。
自定义枚举项的值
需要声明枚举构造函数,并有int类型的参数。
每个枚举项都可以理解成是这个枚举类型,包含了其定义的属性和方法。
public enum MyEnum {
First(100), Second(200), Third(300);
int state;
MyEnum(int i) {
state = i;
}
public int getState(){
return state;
}
}
自定义方法
枚举类
方法名称 |
描述 |
values() |
以数组形式返回枚举类型的所有成员 |
valueOf() |
将普通字符串转换为枚举实例 |
compareTo() |
比较两个枚举成员在定义时的顺序 |
ordinal() |
获取枚举成员的索引位置 |
EnumMap与EnumSet
EnumMap 是专门为枚举类型量身定做的 Map 实现,HashMap 只能接收同一枚举类型的实例作为键值。
EnumSet 是枚举类型的高性能 Set 实现,它要求放入它的枚举常量必须属于同一枚举类型。
概念
编译期是指把源码交给编译器编译成计算机可以执行的文件的过程
运行期是把编译后的文件交给计算机执行
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
作用
运行期判断对象的类
运行期创建对象
运行期判断类的成员变量和方法
运行期调用对象的方法
生成动态代理 Java基础之反射生成JDK动态代理 - 技术小白丁 - 博客园 (cnblogs.com)
优点
动态获取类实例,提高灵活性和扩展性;
与动态编译结合,实现无比强大的功能;
运行时装配,实现组件间解耦,更容易实现面向对象。
缺点
性能损耗
破坏封装性,导致安全问题
Class
// 1. 通过类型class静态变量
Class clz1 = String.class;
String str = "Hello";
// 2. 通过对象的getClass()方法
Class clz2 = str.getClass();
获取类对象成员
getConstructor/getConstructors/getDeclaredConstructors
getMethod/getMethods/getDeclaredMethods
getField/getFields/getDeclaredFields
有Declare:类自身声明的构造函数、方法、成员变量,不包括父类定义
无Declare:类可以访问的public的,包括父类定义的
setAccessible
con.setAccessible(true); // 设置允许访问 private 成员
method.setAccessible(true); // 设置为允许访问private方法
field.setAccessible(true);
反射操作数组
获取数组元素类型
Class> arrayClass = instanceArray.getClass().getComponentType();
Array类常用API
get(Object array,int index):获取数组中指定位置的内容。
newInstance(Class> componenType,int length):根据指定类型和指定长度,开辟一个新的数组
set(Object array,int index,Object value):修改数组中指定位置的内容
反射操作泛型
Java采用泛型擦除机制来引入泛型,泛型仅是给编译器Javac使用的,确报数据的安全性和免去数据类型强制转换的麻烦。但是编译一旦完成,所有和反省有关的类型全部被擦除。
为了通过反射操作泛型类型,Java引入了以下类型。他们不是Class类中的类型,但是和原始类型起名。
ParameterizedType 一种参数化类型,比如Collection< String >
GenericArrayType 一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable 是各种类型变量的公共父接口
WildcardType 代表一种通配符类型表达式
基本注解
@Override:重写父类方法
@Deprecated:元素已过时,IDEA删除线标注,编译器警告
@SuppressWarnings:抑制警告
@SafeVarargs:抑制参数类型错误警告
@FunctionalInterface:为了Lambda定义的函数式接口
元注解
@Documented:注释,doc文档
@Target:作用范围
@Retention:生命周期,注解被保留的时间长短
@Inherited:继承
@Repeatable:重复
@Native
自定义注解
根据注解是否包含成员变量,可以分为如下两类
标记注解:没有定义成员变量的注解类型被称为标记注解。这种注解仅利用自身的存在与否来提供信息,如前面介绍的 @Override、@Test 等都是标记注解。
// 定义一个简单的注解类型
public @interface Test {
}
元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。
public @interface MyTag {
// 定义带两个成员变量的注解
// 注解中的成员变量以方法的形式来定义
String name();
int age();
}
反射获取
getAnnotations()