面向过程:oriented-procedure,执行者思维,关注问题的实现步骤(怎么实现),适合简单、不需要协作、关注如何执行的事务
面向对象:oriented-object,设计者思维,从整体上分析系统,但实现部分依然依靠面向过程处理,适合解决复杂、需要协作的问题
面向对象离不开面向过程
this的本质是创建好的对象的地址
创建对象的步骤:
静态属性和静态方法属于类,非静态属性属于对象;
静态方法中不能调用非静态方法;
静态初始化块用于类的初始化,在静态初始化块中不能直接访问非静态成员
类型 | 声明位置 | 从属于 | 生命周期 | 内存位置 |
---|---|---|---|---|
局部变量 | 方法或语句块内部 | 方法/语句块 | 声明位置开始到方法或语句块执行完毕 | 方法的栈帧中 |
成员变量(实例变量) | 类内部,方法外部 | 对象 | 对象创建开始到对象消失结束 | 堆里对象的内存位置 |
静态变量 (类变量) | 类内部,static修饰 | 类 | 类加载开始,类卸载结束 | 方法区 |
通过package解决类同名和类管理问题,package通常是类的第一句非注释语句;包名由域名倒着写加上模块名
注意:com.lqr和com.lqr.test两个包没有包含关系
//例如
package com.lqr
import static java.lang.Math.*;
import static java.lang.Math.PI;
public class Test{
public static void main(String args[]){
System.out.println(PI);
System.out.println(random());
}
}
定义在类内的类
作用:提供更好的封装,只让外部类访问
public class Outer {
int age=10;
private void show(){
System.out.println("lalala");
}
public class Inner{
int age=20;
public void showInner(){
System.out.println(age);//调用的是自己的属性
System.out.println(Outer.this.age);//调用外部类的重名属性
show();//调用外部类的方法
}
}
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.showInner();
}
}
作用:代码复用和类的扩展;对事务建模
注意:
对象 instanceof 类,判断对象是否是类或子类所创建的
=:方法权限修饰符(public/protected/private),子类>=父类
作用:
实现代码复用的方法:继承和组合,组合更加灵活
组合:将父类对象作为子类属性,子类通过调用属性来获得父类属性和方法
public class Student extends Person{
Person p;
int grade;
Student(int grade, int age, String name){
this.grade = grade;
this.p.age = age;
this.p.name = name;
}
}
继承:is a
组合:has a
IDEA快捷键: 类的结构视图(alt+7)
System.out.println(对象)就是打印一个对象的类别+地址
重写toString()方法可以在类里面自定义打印的内容
@Override
public String toString() {
return "Student{" +
"grade=" + grade +
", name='" + name + '\'' +
", age=" + age +
", money=" + money +
'}';
}
Object的equals方法是用==判断两个对象地址是否一样(是不是同一个对象);
重写equals方法,用来判断两个对象是不是逻辑上相等:右键constructer生成
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person p = (Person) o;
return id == p.id;
}
super可以看作是直接父类对象的引用,调用super可以访问父类中被子类覆盖的方法或属性
构造方法的第一行若没有显式调用super()或this(),默认调用super来调用父类的无参构造方法
public abstract class Person {
String name;
int age;
int id;
public Person(int id) {
this.id = id;
System.out.println("person constructing");
}
}
public class Student extends Person{
int grade;
public Student(int id) {
super(id);
System.out.println("constructing student");
}
}
public static void main(String[] args) {
new Student(1000);
}
//person constructing
//constructing student
绘制类的继承树:类文件右键->show Diagram->右键->show implements
高内聚、低耦合
优点:提高代码安全性;提高代码复用性;高内聚(便于修改代码,提高可维护性);低耦合(简化外部调用,便于协作扩展)
修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
---|---|---|---|---|
private | * | |||
default | * | * | ||
protected | * | * | * | |
public | * | * | * | * |
同一个包内,子类可以访问父类和父类对象的protected
不同包内,子类可以通过super访问父类的protected,但不能访问父类对象的protected
同一个方法调用,对象不同可能会有不同行为
注意:
public class Person {
public void rest(){
System.out.println("person rest");
}
}
public class Teacher extends Person{
@Override
public void rest(){
System.out.println("teacher rest");
}
}
public class Student extends Person{
@Override
public void rest(){
System.out.println("student rest");
}
static void rest(Person p){
p.rest();
}
public static void main(String[] args) {
Person p = new Person();
Student s = new Student();
Teacher t = new Teacher();
p.rest();//person rest
s.rest();//student rest
t.rest();//teacher rest
p = new Student();
p.rest();//student rest
rest(s);//student rest
rest(t);//teacher rest
}
}
抽象方法:只有声明,没有方法体,只定义规范,非抽象子类必须实现
抽象类:
比抽象还抽象,全面实现了规范和实现的分离
普通类都是具体实现;抽象类既有具体实现也有抽象方法;接口都是抽象方法(JDK8以后也有具体实现)
接口的方法都是public abstract,属性都是public static final
接口可以多继承
允许在接口内定义默认方法(扩展方法)和静态方法
default关键字声明默认方法,可以有具体实现,类通过实现接口继承和重写默认方法,通过对象调用
接口中的静态方法从属于接口(接口也是特殊的类),通过接口名调用
若子类中定义了同名的静态方法,则该方法从属于子类,通过子类名调用(不叫重写)
A接口继承B接口和C接口(extends),D类实现A接口时(implements)需要实现ABC接口的所有方法
不含参数的函数有一个隐式参数为this,在调用时会在虚拟机栈中新建一个栈帧,存储所需的变量
若栈中变量指向对象,对象存储在堆中
若对象的属性不是基本类型,则指向方法区中属于这个类的区域中,方法体也存在这里
方法调用结束后,从栈中退出
创建对象时,调用的构造方法也会新开辟一块栈帧,创建结束后退出
方法区是一种规范,可以有不同实现,jdk7/jdk8之前和之后都是不同的
java的内存管理很大程度指的是堆中的对象管理
对象空间的分配:使用new关键字创建对象
对象空间的释放:赋值null,垃圾回收器负责回收所有不可达对象的内存空间
堆中每个对象对应一个引用计数器,引用计数器的值为0时,垃圾回收器认为该对象是无用对象并进行回收
优点:算法简单
**缺点:**无法识别循环引用的无用对象
将所有引用关系看作一张图,从根节点开始遍历引用的节点,结束后没有被访问过的节点就是没有被引用的节点,即无用的节点
将对象分为年轻代、年老代、永久代,并放到不同区域
JVM将堆内存划分为Eden、Survivor和Tenured/Old空间
//例子
String str = ""
for(int i=0; i<10000; i++){
str += i;//相当于产生了10000个string对象
}
不可变字符序列,位于java.lang包,java默认导入
java字符串是unicode字符序列
java没有内置字符串类型,String只是一个预定义的类,字符串是String的一个实例
//字符串内容存在方法区中的常量池里
String s = new String("aaa");//s指向堆中的对象指向常量池
String ss = "aaa";//ss直接指向方法区内的常量池
System.out.println(s==ss);//false
System.out.println(s.equals(ss));//true
String是不可变字符序列,所有替换、截取、去空格、转换大小写都是生成了新的字符串
private final char value[];
String s1 = "hello"+" java";//编译器在编译时直接拼接字符串
String s2 = "hello java";
System.out.println(s1==s2);//true
String s3 = "hello";
String s4 = " java";
String s5 = s3+s4
System.out.println(s5==s2);//false
可变字符序列,效率低但线程安全
String ss = "";
long num1 = Runtime.getRuntime().freeMemory();
long time1 = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
ss+=i;
}
long num2 = Runtime.getRuntime().freeMemory();
long time2 = System.currentTimeMillis();
System.out.println("内存占用:"+(num1-num2));
System.out.println("耗时:"+(time2-time1));
StringBuilder sb = new StringBuilder("");
num1 = Runtime.getRuntime().freeMemory();
time1 = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
sb.append(i);
}
num2 = Runtime.getRuntime().freeMemory();
time2 = System.currentTimeMillis();
System.out.println("内存占用:"+(num1-num2));
System.out.println("耗时:"+(time2-time1));
特点:
for(int i:a)
遍历过程不能修改元素值,只适合读取
System类中的arraycopy(src, scr_start, targ, targ_start, length)方法
int[] a = {0,8,67};
int[] b = {1,2,3,4};
System.out.println(Arrays.toString(b));
System.arraycopy(a, 0, b, 1, 3);
System.out.println(Arrays.toString(b));
打印:System.out.println(Arrays.toString(b));
排序:Arrays.sort(b)
赋值:Arrays.fill(a, start, ends, value)
二分查找:Arrays.binarySearch(a, value)
int[][] a =new int[3][];
a[0] = new int[2];
a[1] = new int[3];
a[2] = new int[4];
Object[] o = {100, "li", 2022-09-07, "arefeasdc"}
Object[][] os = new Object[8][];
os[0] = o
public int compareTo(Object obj)
将当前对象和obj对象比较,大于返回1,等于返回0,小于返回-1
包装类均位于java.lang包中
Number类是抽象类,抽象方法intValue~doubleValue由数字类实现,可以实现数字类型的互相转换
Integer a = new Integer(2);//不推荐
Integer b = Integer.valueOf(2);//基本类型转化为对象
int a = b.intValue();//对象转化为基本类型
Double d = b.doubleValue();//Interger转化为Double
Integer c = Integer.parseInt("123");//String转化为Integer
Integer cc = new Integer("222");
String str = b.toString();//Integer转化为String
System.out.println(Integer.MAX_VALUE);//能表示的最大值
JDK1.5以前,Integer a = 2
是错误的
JDK1.5以后,JVM执行了Integer a = Integer.valueOf(2)
,将基本类型转化为包装类,称为自动装箱
int b = a
,JVM执行了int b = a.intValue()
,称为自动拆箱
Integer a1 = Integer.valueOf(4000);
Integer a2 = Integer.valueOf(4000);
Integer b1 = Integer.valueOf(22);
Integer b2 = Integer.valueOf(22);
System.out.println(a1==a2);//false
System.out.println(b1==b2);//true
System.out.println(a1.equals(a2));//true
当数字在-128~127时,返回的是缓存数组中的某个元素,因此b1和b2指向的同一个对象
1970年1月1日00:00:00作为基准时间,单位是毫秒
long类型表示时间
long a = Long.MAX_VALUE/365/24/60/60/1000L;
System.out.println(a);//大约能表示2.9亿年
long time = System.currentTimeMillis();//当前时刻毫秒数
要点:new Date() getTime()
Date d1 = new Date();//默认当前时刻
System.out.println(d1);//Thu Dec 01 19:51:48 CST 2022
System.out.println(d1.getTime());//1669895508068
Date d1 = new Date(1000L*3600*24*365*30);
System.out.println(d1);//Sat Dec 25 08:00:00 CST 1999
System.out.println(d1.getTime());//946080000000
实现时间对象和字符串的互相转化:DateFormat是抽象类,一般用SimpleDateFormat
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat df2 = new SimpleDateFormat("yyyy年MM月dd日");
String time = df.format(new Date());
System.out.println(time); //2022-12-01 20:01:03
System.out.println(df2.format(new Date()));//2022年12月01日
System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date()));//08:01:03
try {
Date d = df.parse("1999-10-30 09:24:35");
System.out.println(d); //Sat Oct 30 09:24:35 CST 1999
} catch (ParseException e) {
throw new RuntimeException(e);
}
SimpleDateFormat df = new SimpleDateFormat("今天是今年的第w周,这个月的第W周," +
"今年的第D天,这个月的第d天,");
System.out.println(df.format(new Date()));//今天是今年的第49周,这个月的第1周,今年的第335天,这个月的第1天
关于日期计算的相关功能
Calendar是抽象类,子类为GregorianCalendar
月份用0~11表示,Calendar中用常量JANUARY/FEBRUARY…表示
GregorianCalendar g = new GregorianCalendar(1999,10,30,9,30,27);
System.out.println(g.get(Calendar.YEAR));
System.out.println(g.get(Calendar.MONTH));
System.out.println(g.get(Calendar.DAY_OF_MONTH));
System.out.println(g.get(Calendar.DATE)); //与Calendar.DAY_OF_MONTH同义
System.out.println(g.get(Calendar.DAY_OF_WEEK)); //周日-周六依次为1-7
g.set(Calendar.YEAR, 2022);
g.set(Calendar.MONTH, Calendar.DECEMBER);
g.add(Calendar.YEAR, 2);
g.add(Calendar.MONTH, -2);
Math.abs() | Math.sqrt() | Math.asin() | Math.sin() | Math.pow() | Math.min() | Math.PI | Math.E |
---|---|---|---|---|---|---|---|
Math.ceil() | 比a大的最小整数,返回double | Math.floor() | 比a小的最大整数 | Math.round() | 四舍五入 | Math.random() | 产生[0,1)之间的随机小数 |
Random r = new Random();
System.out.println(r.nextDouble());//[0,1)
System.out.println(r.nextInt());//int允许范围内
System.out.println(r.nextInt(10));//[0,10)
System.out.println(r.nextInt(10)+9);//[9,19)
System.out.println(r.nextBoolean());
获得项目所在路径:
System.out.println(System.getProperty("user.dir"));
public static void main(String[] args) {
File f = new File("D:\\myfiles\\数模");
printf(f, 0);
}
public static void printf(File f, int level){
for (int i = 0; i <= level; i++) {
System.out.print("-");;
}
System.out.println(f.getName());
if(f.isDirectory()){
File[] fs = f.listFiles();
for (File ff:fs) {
printf(ff, level+1);
}
}
}
需要定义一组常量时使用
枚举本质上是类,每个成员就是一个实例,默认public static final修饰
enum Season{SPRING, SUMMER, AUTUMN, WINTER}
System.out.println(Season.SPRING);//SPRING
for(Season s:Season.values()){
System.out.println(s);
}
数据类型参数化,调用泛型时必须传入实际类型
泛型类,泛型接口,泛型方法
类型擦除:编码时采用泛型写的类型参数,编译器在编译时会去掉,编译后的class不包括泛型定义,会被编译器替换成具体的Object
注意
T test = new T()
是错误的定义:泛型字符可以是任何标识符,一般采用E,T,K,V,N,?
public class 类名<泛型标识符>{}
public class Gener<T> {
private T flag;
public T getFlag() {
return flag;
}
public void setFlag(T flag) {
this.flag = flag;
}
public static void main(String[] args) {
Gener<String> gen = new Gener<>();
gen.setFlag("flag");
}
}
public interface 类名<泛型标识符>{}
public interface Gener<T> {
T getName(T name);
}
public class genrimpl implements Gener<String>{
@Override
public String getName(String name) {
return name;
}
public static void main(String[] args) {
genrimpl g = new genrimpl();
g.getName("name");
Gener<String> gen = new genrimpl();//使用泛型接口时指定类别
gen.getName("name");
}
}
将方法的参数定义成泛型,调用时接收不同类型的参数
非静态泛型方法既可以自己定义,也可以通过泛型类定义
public
public
public class Gener{
public <T> void setName(T name){
System.out.println(name);
}
public <T> T getName(T name){
return name;
}
public static void main(String[] args) {
Gener g = new Gener();
g.setName("lqr");
g.setName(123);
Gener g2 = new Gener();
String n = g2.getName("lqr");
System.out.println(n);
Integer nn = g2.getName(123);
System.out.println(nn);
}
}
静态方法不能使用类上定义的泛型
public static
public class Gener{
public static <T> void test(T name){
System.out.println(name);
}
public static <T> T getFlag(T flag){
return flag;
}
public static void main(String[] args) {
Gener.test(123);
Boolean f = Gener.getFlag(true);
}
}
public
public class Gener{
public <T> void test(T... args){
for(T t:args){
System.out.println(t);
}
}
public static void main(String[] args) {
new Gener().test(123, "lllaaa", 1.2);
}
}
"?“表示类型通配符,代替具体类型,只能在”<>"中使用,可以解决类型不确定时候的问题
public void xxx(Generic gen){}
public class Gener<T>{
private T flag;
public T getFlag() {
return flag;
}
public void setFlag(T flag) {
this.flag = flag;
}
public void showFlag(Gener<?> gen){
System.out.println(gen.getFlag());
}
public static void main(String[] args) {
Gener<String> g1 = new Gener<>();
g1.setFlag("20");
new Gener<>().showFlag(g1);
Gener<Integer> g2 = new Gener<>();
g2.setFlag(20);
new Gener<>().showFlag(g2);
}
}
通配符的类型是T或T的子类,T接口或T接口的子接口,适用于泛型类
public void XXX(Generic gener){}
通配符类型是T或T的父类,T接口或T接口的父接口,不适用于泛型类
public void XXX(Gneric gen){}
存储有序,可重复,动态数组
List接口中增加的方法(因为list是有序的,添加的方法都是跟元素位序有关的):
ArrayList类是List接口的实现类,底层是用数组实现的存储
特点:查询效率高,增删效率低,线程不安全
//实例化容器 接口=new 实现类
List<String> arrlist = new ArrayList<String>();
boolean flag = arrlist.add("test1");
arrlist.add(0,"test2");//没有返回值,索引数值不能大于元素个数
System.out.println(flag);//true
arrlist.remove(1);//返回移除的值;同arrlist.remove("test1"),返回boolen
String s = arrlist.set(0, "lalala");//替换,不能添加,Index不能超过长度
System.out.println(arrlist.contains("lalala"));
arrlist.clear();
System.out.println(arrlist.isEmpty());
System.out.println(arrlist);
Object[] ss = arrlist.toArray();//不能使用String[]强制转换,会出现异常
String[] ss = arrlist.toArray(new String[arrlist.size()]);
List<String> A = new ArrayList<String>();
A.add("a");
A.add("b");
A.add("c");
List<String> B = new ArrayList<String>();
B.add("d");
B.add("b");
B.add("c");
A.addAll(B);
System.out.println(A);//[a, b, c, d, b, c]
A.removeAll(B);
System.out.println(A);//[a]
A.retainAll(B);
System.out.println(A);//[]
JDK1.7中使用立即加载,初始化时分配默认10个大小的空间(长度为10),JDK1.8之后使用延迟加载,初始化默认为空(长度为0)
private static final int DEFAULT_CAPACITY = 10;//创建时的默认长度
transient Object[] elementData;//元素操作存放的数组
private int size;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {//无参构造
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
底层用数组实现,线程安全但效率低,大部分与ArrayList类似
public synchronized boolean add(E e) {}
,add()方法有并行化处理,是线程安全的Vector的一个子类
通过5个方法对Vector进行扩展,实现堆栈操作
empty()、peek()、pop()、push()、search()
Stack<Integer> stack = new Stack<>();//不再是用List定义
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(stack.pop());
System.out.println(stack.empty());
System.out.println(stack.search(2));
System.out.println(stack.search(9));
System.out.println(stack.peek());
System.out.println(stack);
public static boolean sysm(String s){
Stack<Character> stack = new Stack<>();
for(int i = 0; i<s.length(); ++i){
char c = s.charAt(i);
if(c=='(')
stack.push(')');
if(c=='[')
stack.push(']');
if(c=='{')
stack.push('}');
if(c==')'||c==']'||c=='}'){
if(stack.empty()||c!=stack.peek())
return false;
else
stack.pop();//匹配的字符需要出栈
}
}
return stack.empty();//匹配结束后需要满足栈空
}
底层使用双向链表实现,查询效率低,增删效率高,线程不安全
使用与ArrayList基本一致,额外添加的方法使用,需要在声明时使用LinkedList变量
方法 | 说明 |
---|---|
void addFirst(Ee) | 将指定元素插入到开头 |
void addLast(E e) | 将指定元素插入到结尾 |
getFirst() | 返回此列表的第一个元素 |
getLast() | 返回此列表的最后一个元素 |
removeFirst() | 移除此列表中的第一个元素,并返回这个元素 |
removeLast() | 移除此列表中的最后一个元素,并返回这个元素 |
E pop() | 从此列表所表示的堆栈处弹出一个元素,等效于removeFirst |
void push(E e) | 将元素推入此列表所表示的堆栈这个等效于addFisrt(E e) |
boolean isEmpty() | 判断列表是否包含元素,如果不包含元素则返回true |
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
private static class Node<E> {
Eitem;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
指定位置添加元素时寻找元素位置——二分:
寻找索引位置时先对size进行>>1,与index进行大小比较;
若index在前半部分从前往后找,否则从后往前找
存储无序,不可重复,“集合”,List接口有相比Collection新增的方法,但Set没有
允许null元素出现,采用哈希算法实现,底层用HashMap实现,查询和增删效率都高
Set
HashSet存储两个属性相同的对象时,无法分别出这是两个相同的元素,因为二者的HashCode值不同;因此需要在类中重写equals方法
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
可以对元素进行排序,实际是用TreeMap实现的,通过key存储Set的元素
排序规则:元素自身比较或通过比较器比较
TreeSet是通过实现NavigableMap间接实现TreeMap
private transient NavigableMap<E, Object> m;
private static final Object PRESENT = new Object();
基于key+value的结构存储数据,类似函数f(x)
不能包含重复的键,可以包含重复的值,每个键只能对应一个值
方法 | 说明 |
---|---|
V put (K key,V value) | 把key与value添加到 Map集合中,若key已存在,则更新对应的value |
void putAll(Map m) | 从指定Map中将所有映射关系复制到此Map中,key相同的value值会被更新 |
V remove (Object key) | 删除 key对应的value |
v get(Object key) | 根据指定的key,获取对应的value |
boolean containsKey(Object key) | 判断容器中是否包含指定的key |
boolean containsValue(Object value) | 判断容器中是否包含指定的value |
Set keySet() | 获取Map集合中所有的 key,存储到Set集合中 |
Set |
返回一个Set基于Map.Entry类型包含Map中所有映射。 |
void clear() | 删除Map中所有的映射 |
Map接口的实现类,采用哈希算法实现,查删改效率都很高
Map
Set<String> keys = map.keySet();
for(String s:keys){
String v = map.get(s);
}
Set<Map.Entry<String, Integer>> entryset = map.entrySet();
for(Masp.Entry<String, Integer> entry:entryset){
String key = entry.getKey();
Integer value = entry.getValue();
}
底层采用哈希表,本质是数组+链表
JDK1.8特性:链表长度大于8时,会转换为红黑树来减少查询时间;红黑树节点小于6时转换为链表
static final int DEFAULT_INITIAL_CAPACITY=1<<4;//16,数组初始长度
static final int MAXIMUM_CAPACITY=1<<30;//数组长度上限
static final float DEFAULT_LOAD_FACTOR=0.75f;//扩容负载因子,当数组达到这个长度时进行扩容,13
static final int TREEIFY_THRESHOLD=8;//转换为红黑树的阈值
static final int UNTREEIFY_THRESHOLD=6;//转换为链表的阈值
static final int MIN_TREEIFY_CAPACITY=64;//数组长度达到64才会将链表转为红黑树
transient int size;
transient Node<K,V>[] table;
链表使用Node类,红黑树使用TreeNode类,继承Node类
TreeNode类继承Entry类继承Node类实现Entry接口;
数组初始化采用延迟初始化,等有内容了再调用resize()方法扩容和初始化数组;
计算hash值:
效率低于TreeMap,但可以对键值进行排序,底层基于红黑树实现
与TreeSet相同,可以是类实现Comparable接口或自定义比较器作为TreeMap的初始化参数
firstKey():获得最小key值
lastKey():获得最大key
Collection接口继承了Iterator接口,接口中包括iterator抽象方法,返回一个Iterator对象,包含三个方法用于单例容器的迭代处理
List<String> list = new ArrayList<>();
list.add("123");
list.add("13");
list.add("23");//set容器类似
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String s = iterator.next();
if(s.equals("123"))
iterator.remove();
}
System.out.println(list);
注意:
Collections是一个工具类,它提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。该类中所有的方法都为静态方法。
常用方法:
List<String> list = new ArrayList<>();
list.add("z");
list.add("d");
list.add("a");
list.add("123");
list.add("32");
list.add("2");
Collections.sort(list);
Collections.shuffle(list);
System.out.println(list);
annotaion是JDK5.0引入的新技术
作用:对程序作出解释(类似注释);可以被其他程序(如编译器)读取
格式:“@注释名”,可以添加参数值
例如@SuppressWarnings(value="unchecked")
public class annotation {
@myannotaion(age=22)
public void test(){
}
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface myannotaion{
String name() default "lqr";//注解的参数
int age();
}
允许程序在执行期借助reflection API获得任何类的内部消息,并能直接操作任意对象的内部属性及方法
**作用:**在运行时判断任意一个对象所属的类、构造任意一个类的对象、判断类具有的成员变量和方法、获取泛型信息、调用对象的成员变量和方法、处理注解、生成动态代理
**优点:**实现动态创建对象和编译
**缺点:**影响性能
try {
Class c1 = Class.forName("com.lqr.basic.User");
Class c2 = Class.forName("com.lqr.basic.User");
System.out.println(c1);//class com.lqr.basic.User
//一个类在内存中只有一个Class对象
//一个类被加载后,类的整个结构会被封装在Class对象中
System.out.println(c1.hashCode()==c2.hashCode());//true
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
//1. 通过对象获得Class
People p = new People("111");
Class pc1 = p.getClass();
//2. forName获得Class
try {
Class pc2 = Class.forName("com.lqr.basic.People");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
//3. 通过类名.class获得
Class pc3 = People.class;
//4. 内置对象的TYPE属性
Class ic = Integer.TYPE;
拥有Class对象的类型: class、interface、数组、enum、annotaion、基本数据类型、void
类的主动引用——一定会初始化类:
类的被动引用:
作用:将class文件字节码内容加载到内存,在堆中生成这个类的java.lang.Class对象,
类型:根加载器(C++编写、无法直接获取)、扩展加载器、系统类加载器
双亲委派机制:若根加载器中含有的包,则不会调用扩展加载器中的同名包,以此类推
getName()
:获得包名+类名
getSimpleName()
:获得类名
getFields(); getField(String); getMethods(); getMethod(String, paramTypes)
:获得public属性或本类及父类的所有方法
getDeclaredFields(); getDeclaredField(String); getDeclaredMethods(); getDeclaredMethod(String, paramTypes)
:获得所有属性或本类的方法
getConstructors(); getDeclaredConstructors()
:获得构造器
//newInstance()创建对象
Class c1 = Class.forName("com.lqr.basic.User");
User u1 = (User)c1.newInstance();//调用类的无参构造器
//通过构造器创建对象
Constructor con = c1.getDeclaredConstructor(String.class, int.class);
User u2 = (User)con.newInstance("lqr", 22);
//通过反射调用方法
User u3 = (User)c1.newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(u3, "lqr");//激活
//通过反射操作属性
User u4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//私有属性或方法不能直接操作,需要关闭程序的安全检测
name.set(u4, "lqr");
反射调用多的话,关闭程序的安全检测能提高效率
java采用泛型擦除机制引入泛型,编译完成后与泛型相关的类型全部擦除
为了通过反射操作泛型,java新增了几种类型代表不能被归一到Class类中的类型:
//获得参数化的泛型
Method method = Test.class.getMethod("test01",Map.class,List.class);
Type[] genParamTypes = method.getGenericParameterTypes();
for(Type genpt:genParamTypes){
System.out.println(genpt);
if(genpt instanceof ParameterizedType){
Type[] actualpts = ((ParameterizedType)genpt).getActualTypeArguments();
for(Type actpt:actualpts){
System.out.println(actpt);
}
}
}
//获得返回值中的参数化泛型
Method method = Test.class.getMethod("test02",null);
Type genReType = method.getGenericReturnType();
if(genReType instanceof ParameterizedType){
Type[] actualrts = ((ParameterizedType)genReType).getActualTypeArguments();
for(Type actrt:actualrts){
System.out.println(actrt);
}
}
Class Stu = Class.forName("com.lqr.reflection.Student");
//获取注解
Annotation[] annos = Stu.getAnnotaions();
for(Annotation ann:annos){
System.out.println(ann);
}
//获取类的注解的参数值
Tablelqr ann = (Tablelqr)Stu.getAnnotation(Tablelqr.class);//参数为注解的class
String value = ann.value();
System.out.println(value);
//获取属性的注解的参数值
Field f = Stu.getDeclaredField("name");
Fieldlqr ann = (Fieldlqr)f.getAnnotation(Fieldlqr.class);
System.out.println(ann.colunm());
System.out.println(ann.len());
System.out.println(ann.type());
程序:指令和数据的有序集合,是静态概念
进程process:执行程序的一次执行过程,是动态概念,是系统资源分配的单位
线程thread:一个进程可以包含若干线程,是CPU调度和执行的单位
真正的多线程指有多个CPU(多核);模拟出来的多线程是在一个CPU的情况下切换的很快
public class myThread extends Thread{
@Override
public void run(){
for(int i=0; i<10; ++i){
System.out.println("我的线程 第"+i+"次");
}
}
public static void main(String[] args){
myThread mt = new myThread();
mt.start();//mt.run()则先执行run()方法,调用start()则交替执行main方法和自定义线程
for(int i=0; i<10; ++i){
System.out.println("main线程 第"+i+"次");
}
}
}
注意:创建线程后不一定立即执行,执行时间由CPU调度
//抢票模拟,多个线程操作同一个资源,线程不安全
public class myRunnable implements Runnable{
private int ticketNum=10;
@Override
public void run(){
while(true){
if(ticketNum<=0)
break;
try{
Thread.sleep(200);//模拟延时
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNum--+"张票");
}
}
public static void main(String[] args){
myRunnable mr = new myRunnable();
new Thread(mr,"抢票器1").start();
new Thread(mr,"抢票器2").start();
new Thread(mr,"抢票器3").start();
}
}
//龟兔赛跑
public class TurRabitRace implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//兔子睡觉
if(Thread.currentThread().getName().equals("rabbit")&&i==20) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//比赛结束
if(over(i))
break;
System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
}
}
private boolean over(int steps){
if(winner!=null)
return true;
if(steps>=100){
winner=Thread.currentThread().getName();
System.out.println(winner+"胜利");
return true;
}
return false;
}
public static void main(String[] args) {
TurRabitRace mr = new TurRabitRace();
new Thread(mr,"turtle").start();
new Thread(mr,"rabbit").start();
}
}
ExecutorService ser = Executors.newFixedThreadPool(1);
Future result = ser.submit();
boolean re = result.get();
ser.shutdownNow();
import java.util.concurrent.*;
public class myCallable implements Callable<Boolean>{
@Override
public Boolean call(){
for(int i=0; i<10; ++i){
System.out.println(Thread.currentThread().getName()+" 第"+i+"次");
}
return true;
}
public static void main(String[] args){
myCallable mc1 = new myCallable();
myCallable mc2 = new myCallable();
ExecutorService ser = Executors.newFixedThreadPool(2);
Future<Boolean> result1 = ser.submit(mc1);
Future<Boolean> result2 = ser.submit(mc2);
try {
boolean re1 = result1.get();
boolean re2 = result2.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
ser.shutdown();
}
}
public class StaticProxy {
public static void main(String[] args) {
//Thread相当于婚庆代理,Runnable的接口实现类相当于真实对象,start相当于marry方法
new Thread(()->System.out.println("i love u")).start();
new WeddingCompany(new You("wzj")).Marry();
}
}
interface Marry{
void Marry();
}
class You implements Marry{
private String name;
public You(String name) {
this.name = name;
}
@Override
public void Marry() {
System.out.println(this.name+"要结婚了");
}
}
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void Marry() {
System.out.println("婚庆公司帮你准备");
this.target.Marry();
System.out.println("婚庆公司帮你收尾");
}
}
作用:避免匿名内部类定义过多;代码简洁;
函数式接口:任何接口如果只包含一个抽象方法,那么它就是函数式接口
函数式接口可以通过lamda表达式创建对象
public class Lamda {
public static void main(String[] args) {
new like().lamda("新建对象");//创建实现类对象再调用方法
ilike like = new ilike() {
@Override
public void lamda(String name) {
System.out.println(name+" implements ilike");
}
};
like.lamda("匿名内部类");
//ilike lamdalike = (String name)->{
// System.out.println(name+" implements ilike");
//};
//简化为
ilike lamdalike = name->System.out.println(name+" implements ilike");
lamdalike.lamda("lamda");
}
}
interface ilike{
void lamda(String name);
}
class like implements ilike{
@Override
public void lamda(String name) {
System.out.println(name+" implements ilike");
}
}
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 合并线程,待此线程执行完后再执行其他线程,其他线程阻塞 |
static void yield() | 暂停当前正在执行的线程对象,并重新调度线程 |
void interrupt() | 中断线程,别用这个方式 |
boolean isAlive() | 测试线程是否处于活动状态 |
不推荐使用stop()或destroy()方法(已废弃),建议让线程自己停下来
可用使用标志位标志线程是否运行,提供public stop()方法更改标志位,run()方法利用标志位判断是否运行
slepp()存在异常interruptedException;
sleep()时间达到后线程进入就绪状态;
slepp()可以模仿网络延时、倒计时等;
每个对象都有一个锁,sleep()不会释放锁
让当前正在执行的线程停止,但不阻塞,将线程从运行状态转为就绪状态;
礼让不一定成功,CPU来负责重新调度线程,可能调度礼让的线程,也可能调度其他线程;
强制执行时其他线程是阻塞状态,类似插队
join()会抛出InterruptedException异常
Thread.State st = thread.getState();
终止TERMINATED,阻塞TIMED_WARNING,创建NEW,就绪RUNNABLE
线程优先级用数字表示1~10:
Thread.MIN_PRIORITY=1;
Thread.MAX_PRIORITY=10;
Thread.NORM_PRIORITY=5;
getPriority()获取优先级,setPriority()改变优先级
优先级低只是被调度的概率低,不一定不会被调用
线程分为用户线程和守护线程,正常的线程都是用户线程
虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕,用户线程全部执行完毕后程序就终止
例如,后台操作日志、监控内存、垃圾回收
thread.setDaomon(true);//设置为守护线程
并发:同一个对象被多个线程同时操作
线程同步:并发时,某些线程想修改这个对象时,需要线程同步;线程同步就是一种等待机制,需要队列和锁(每个对象都有一把锁)
当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁
问题:
synchronized方法:
必须获得调用该方法的对象的锁才能执行,否则线程会阻塞;
方法一旦执行就独占该锁,直到方法返回才释放
缺点:影响效率,只有修改的代码里面需要锁,只读代码添加锁的话会浪费资源
synchronized (Obj){}
,Obj称之为同步监视器,可以是任何对象﹐但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身或者是class
同步监视器的执行过程:
两个或多个线程都在等待对方释放资源,都停止执行
同一个同步块有两个以上对象的锁时,可能发生死锁
JDK5.0开始,通过显式定义同步锁对象来实现同步
ReentrantLock类实现了Lock接口,拥有与synchronized相同的并发性和内存语义,可以显式加锁和释放锁
public class Lock {
public static void main(String[] args) {
BuyTicket buyer = new BuyTicket();
new Thread(buyer).start();
new Thread(buyer).start();
new Thread(buyer).start();
}
}
class BuyTicket implements Runnable {
private int ticketNum=10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try {
lock.lock();
if(ticketNum>0){
Thread.sleep(1000);
System.out.println(ticketNum--);
} else {
System.out.println("sold out");
break;
}
}catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
}
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止;
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止.
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿数据
使用wait()
方法和notifyAll()
方法
public class ProduceConsume {
public static void main(String[] args) {
container con = new container();
new producer(con).start();
new consumer(con).start();
}
}
class producer extends Thread{
container con;
public producer(container con) {
this.con = con;
}
public void run(){
for (int i = 0; i < 10; i++) {
con.push(new production(i));
System.out.println("生产了"+i+"个产品");
}
}
}
class consumer extends Thread{
container con;
public consumer(container con) {
this.con = con;
}
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("消费了第"+con.pop().id+"个产品"); ;
}
}
}
class production {
int id;
public production(int id) {
this.id = id;
}
}
class container{
production[] pro = new production[10];
int size=0;
public synchronized void push(production p){
if(size==pro.length){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
pro[size++] = p;
this.notifyAll();
}
public synchronized production pop(){
if(size==0){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
production p = pro[--size];
this.notifyAll();
return p;
}
}
package com.lqr.Thread;
public class ActorAudian {
public static void main(String[] args) {
show ss = new show();
new Actor(ss).start();
new Audian(ss).start();
}
}
class Actor extends Thread{
show ss;
public Actor(show ss) {
this.ss = ss;
}
public void run(){
for (int i = 0; i < 10; i++) {
if(i%2==0){
this.ss.show("新白娘子传奇播放中");
}else{
this.ss.show("孩子咳嗽老不好");
}
}
}
}
class Audian extends Thread{
show ss;
public Audian(show ss) {
this.ss = ss;
}
public void run(){
for (int i = 0; i < 10; i++) {
this.ss.see();
}
}
}
class show{
String name;
boolean flag=true;
public synchronized void show(String name){
if(!flag){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("演员表演"+name);
this.notifyAll();
this.name = name;
this.flag = !this.flag;
}
public synchronized void see(){
if(flag){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("观众观看"+name);
this.notifyAll();
this.flag = !this.flag;
}
}
经常创建销毁或线程使用的资源量大,影响性能
提前创建好多个线程放入线程池,使用时直接获取,使用完放回线程池中
好处:提高响应速度;降低资源消耗;便于线程管理
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new myThread());
service.shutdown();