JavaSE下篇
1、Java的多线程
1、线程的创建和使用
-
在JDK1.5以前,有两种实现方式:
-
继承Thread类,重写run方法,然后调用线程对象的start方法,
-
实现Runable接口,重写接口的run方法,相比于继承,推荐实现接口
-
-
在JDK1.5,也有两种新的方式
-
实现Callable接口,它比Runable接口更加强大:它可以有返回值、抛异常、支持泛型,实现代码如下:
//自定义类实现Callable接口,实现call方法,方法可以有返回值 public class MyCallable<String> implements Callable<String> { @Override public String call() throws Exception { System.out.println("线程调用成功!"); return result; } } //该类的使用较为复杂,通常有以下步骤: public static void main(String[] args) throws InterruptedException, ExecutionException { MyCallable<String> mc=new MyCallable<String>();//线程类对象 FutureTask<String> ft=new FutureTask<String>(mc);//创建FutureTask类对象ft,且将线程类对象作为构造器参数传入其中 new Thread(ft).start();//将FutureTask类的实例注册进入Thread中运行 String result=ft.get();//可以使用其get方法获得线程的返回值 System.out.println(result); }
-
使用线程池:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。所以我们可以提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用,创建代码如下:
//Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池,提供了如下静态方法 Executors.newCachedThreadPool();//创建一个可根据需要创建新线程的线程池 Executors.newFixedThreadPool(n);//创建一个可重用固定线程数的线程池 Executors.newSingleThreadExecutor();//创建一个只有一个线程的线程池 Executors.newScheduledThreadPool(n);//创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 //具体使用如下:以newCachedThreadPool为例: @Test public void test1() { //1.使用工厂类获取线程池对象 ExecutorService es = Executors.newCachedThreadPool(); //2.提交任务,所以总的也就两步:创建线程池、调用submit方法并传入线程类 es.submit(new MyRunnable()); //当实现的是Callable接口时,只需要接收返回值即可,没有其他的区别 //Future<Boolean> r3 = ser.submit(Mycallable); } //传入参数MyRunnable线程类如下: class MyRunnable implements Runnable{ @Override public void run() { //获取线程的名称,打印一句话 String name = Thread.currentThread().getName(); System.out.println(name + "执行了任务"); } }
-
2、线程的相关方法
-
void start(): 启动线程,并执行对象的run()方法;
-
run(): 线程在被调度时执行的操作;
-
String getName(): 返回线程的名称;
-
void setName(String name):设置该线程名称;
-
static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类;
-
static void yield():线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程;
-
若队列中没有同优先级的线程,忽略此方法;
-
join() :当某个程序执行流中调用其他线程的 join() 方法时,调用线程将 被阻塞,直到 join() 方法加入的 join 线程执行完为止,低优先级的线程也可以获得执行;
-
static void sleep(long millis):(指定时间:毫秒);
-
boolean isAlive():返回boolean,判断线程是否还活着。
-
getPriority() :返回线程优先值 ;
-
setPriority(int newPriority) :改变线程的优先级,优先级有三类:MAX_PRIORITY:10 、MIN _PRIORITY:1 、NORM_PRIORITY:5 。
3、线程同步机制 - 锁:
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的 工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象 加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和 内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以 显式加锁、释放锁。
示例代码如下:这一线程在访问加锁的代码时就保证了线程安全
class A{ private final ReentrantLock lock = new ReenTrantLock(); public void m(){ lock.lock(); try{ //保证线程安全的代码; } finally{ lock.unlock(); //注意:要将unlock()写入finally语句块,以免同步代码有异常 } } }
4、补充说明
-
对于线程的优先级,低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用;
5、线程间通信的方法
wait() 与 notify() 和 notifyAll():
-
wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当 前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有 权后才能继续执行;
-
notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待;
-
notifyAll ():唤醒正在排队等待资源的所有线程结束等待。
2、Java常用类及其方法
1、String类
常用方法:太多了,不用刻意去记,大概知道有写=些什么功能,需要的时候能找到就OK
int length():返回字符串的长度: return value.length char charAt(int index): 返回某索引处的字符return value[index] boolean isEmpty():判断是否是空字符串:return value.length == 0 String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写 String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写 String trim():返回字符串的副本,忽略前导空白和尾部空白 boolean equals(Object obj):比较字符串的内容是否相同 boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写 String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+” int compareTo(String anotherString):比较两个字符串的大小 String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。 String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字 符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。 boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束 boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始 boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始 boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列 时,返回 true int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引 int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出 现处的索引,从指定的索引开始 int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引 int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后 一次出现处的索引,从指定的索引开始反向搜索 注:indexOf和lastIndexOf方法如果未找到都是返回-1 String replace(char oldChar, char newChar):返回一个新的字符串,它是 通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 String replace(CharSequence target, CharSequence replacement):使 用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 String replaceAll(String regex, String replacement) : 使 用 给 定 的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 String replaceFirst(String regex, String replacement) : 使 用 给 定 的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。 String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。 String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
2、StringBuffer(比较重要)
java.lang.StringBuffer代表可变的字符序列,很多方法与String相同,可以对字符串内容进行增删,其底层数组value没有final声明,value可以不断扩容。这是与String的最大区别。
其构造器方法如下:
StringBuffer():初始容量为16的字符串缓冲区 StringBuffer(int size):构造指定容量的字符串缓冲区 StringBuffer(String str):将内容初始化为指定字符串内容 //常用方法有: StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接 StringBuffer delete(int start,int end):删除指定位置的内容 StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str StringBuffer insert(int offset, xxx):在指定位置插入xxx StringBuffer reverse() :把当前字符序列逆转 public int indexOf(String str) public String substring(int start,int end) public int length() public char charAt(int n ) public void setCharAt(int n ,char ch)
三种String类对比一下:
-
String(JDK1.0):不可变字符序列
-
StringBuffer(JDK1.0):可变字符序列、效率低、线程安全
-
StringBuilder(JDK 5.0):可变字符序列、效率高、线程不安全
3、日期时间API
第一次学的时候感觉很混乱,但是,仔细想来,也就那么回事儿。JDK8之前的时间API就不管了吧。。。
三个最常用的类:
-
LocalDate存储日期;
-
LocalTime表示一个时间,而不是日期;
-
LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。
三个类的通用方法有:常用的不多,这里只是列举,用的时候再来查找就是了
-
now() / * now(ZoneId zone) 静态方法,根据当前时间创建对象/指定时区的对象
-
of() 静态方法,根据指定日期/时间创建对象
-
getDayOfMonth()/getDayOfYear() 获得月份天数(1-31) /获得年份天数(1-366)
-
getDayOfWeek() 获得星期几(返回一个 DayOfWeek 枚举值)
-
getMonth() 获得月份, 返回一个 Month 枚举值
-
getMonthValue() / getYear() 获得月份(1-12) /获得年份
-
getHour()/getMinute()/getSecond() 获得当前对象对应的小时、分钟、秒
-
withDayOfMonth()/withDayOfYear()/ withMonth()/withYear() 将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象
-
plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours() 向当前对象添加几天、几周、几个月、几年、几小时
-
minusMonths() / minusWeeks()/ minusDays()/minusYears()/minusHours() 从当前对象减去几月、几周、几天、几年、几小时。
4、Math类
java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回值类型一般为double型。
同样,知道他们的存在,用的时候能找到就行,不要求记得吧
-
abs 绝对值
-
acos,asin,atan,cos,sin,tan 三角函数
-
sqrt 平方根
-
pow(double a,doble b) a的b次幂
-
log 自然对数
-
exp e为底指数
-
max(double a,double b) min(double a,double b)
-
random() 返回0.0到1.0的随机数
-
long round(double a) double型数据a转换为long型(四舍五入)
-
toDegrees(double angrad) 弧度—>角度
-
toRadians(double angdeg) 角度—>弧度
3、枚举类
一个案例代码就能基本使用枚举类了
public class Enumeration03 { public static void main(String[] args) { System.out.println(Season.SPRING);//使用枚举类对象, } } enum Season {//当然可以有public等修饰,这里是以内部类的形式存在的 SPRING("春天", "温暖"),//定义枚举类对象,以, 分隔 ; 结尾,且会自动添加 public static final 修饰 SUMMER("夏天", "炎热"); private String name;//枚举类属性 private String desc; private Season(String name, String desc) {//私有化构造器,这是关键!!! this.name = name; this.desc = desc; } public String getName() {//提供一些公共方法,可选的 return name; } public String getDesc() { return desc; } } //说明:使用 enum 定义的枚举类默认继承了 java.lang.Enum类,因此不能再继承其他类
4、注解
1、使用
基本注解的使用没啥好说的,主要还是使用框架中的注解
2、自定义注解
-
Annotation 注解类使用 @interface 关键字,它自动继承了java.lang.annotation.Annotation接口;
-
注解类的成员变量定义以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,在变量后面可以加上 default 关键字指定默认值;
-
使用注解时必须指定参数值,除非它有默认值。格式是“参数名 = 参数值”;
-
JDK 中有四个元注解,用于修饰其他 Annotation
-
@Retention: 指定该 Annotation 的生命周期, 使用 @Rentention 时必须指定其生命周期:
-
RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),而编译器会直接丢弃这种策略的注释;
-
RetentionPolicy.CLASS:在class文件中有效(即class保留),当运行 Java 程序时, JVM 不会保留注解。这是默认值;
-
RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行 Java 程序时, JVM 会保留注释。程序运行时也能生效。
-
-
@Target:指定被修饰的 Annotation 能用于修饰哪些程序元素,可多选,逗号隔开
-
ElementType.CONSTRUCTOR:用于描述构造器
-
ElementType.METHOD:用于描述方法
-
ElementType.FIELD:用于描述域()
-
ElementType.LOCAL_VARIABLE:用于描述局部变量
-
-
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档,其中定义了Documented的注解必须设置Retention值为RUNTIME。
-
@Inherited: 被它修饰的 Annotation 将具有继承性。如果把标有@Inherited注解的自定义的注解标注在类级别上,子类则可以 继承父类类级别的注解。
-
-
知道如何自定义注解即可,其真正作用要在业务中才能体现。
@MyAnnotation(value="尚硅谷") public class MyAnnotationTest { public static void main(String[] args) { Class clazz = MyAnnotationTest.class; Annotation a = clazz.getAnnotation(MyAnnotation.class); MyAnnotation m = (MyAnnotation) a; String info = m.value(); System.out.println(info); } } @Retention(RetentionPolicy.RUNTIME)//元注解 @Target(ElementType.TYPE) @interface MyAnnotation{//开发中通常是定义成一个单独的类的,使用public修饰 String value() default "auguigu"; }
5、集合
1、集合整体框架
-
Collection接口:单列集合,存储一个一个的对象
-
List接口:存储有序的,可重复的数据
-
实现类有:ArrayList(!)、LinkedList
-
-
Set接口:存储无序的,不可重复的数据
-
实现类有:HashSet、LinkedHashSet
-
-
-
Map接口:双列集合,用来存储一对一对的数据,key-value
-
实现类有HashMap、LinkedHashMap
-
2、集合常用方法
-
Collection
对象.add(Object o)/addAll(Collection e)//想集合中添加元素,默认添加到末尾 对象.size()//返回集合中元素个数 对象.clear()//清空集合元素 对象.isEmpty()//判断集合是否为空 对象.contains(Object o)//判断集合中是否存在某元素,判断方法是调用对象的equals方法,所以判断时需要注意该对象类是否重写了equals方法(没重写就是比较地址,重写了一般就是比较内容) for(Object o : coll){System.out.println(o)}//遍历集合元素,注意这里是将集合中的元素的值一次赋给o,而不是让o指向集合元素 constainsAll(Collection coll)//判断当前集合中是否包含coll的所有元素 rentainAll(Collection coll)//求当前集合与coll的共有集合,返回给当前集合 remove(Object obj)//删除集合中obj元素,若删除成功,返回ture否则 removeAll(Collection coll)//从当前集合中删除包含coll的元素 equals(Object obj)//判断集合中的所有元素 是否相同 hashCode()//返回集合的哈希值 toArray(T[] a)//将集合转化为数组
-
List
void add(int index, E element) // 在列表的指定位置添加元素(第一个参数是下标) Object get(int index) // 根据下标获取元素(元素的下标,从0开始,以1递增) int indexOf(Object o) //获取指定对象第一次出现的索引 int lastIndexOf(Object o) //获取指定对象最后一次出现的索引 Object remove(int index) //删除指定下标位置的元素 Object set(int index, Object element) //修改指定位置元素(元素下标,修改的值)
-
Set
//没有特别的方法,就是collection接口中的方法
-
-
Map
添加、删除、修改类: Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中 void putAll(Map m):将m中的所有key-value对存放到当前map中 Object remove(Object key):移除指定key的key-value对,并返回value void clear():清空当前map中的所有数据 元素查询类: Object get(Object key):获取指定key对应的value boolean containsKey(Object key):是否包含指定的key boolean containsValue(Object value):是否包含指定的value int size():返回map中key-value对的个数 boolean isEmpty():判断当前map是否为空 boolean equals(Object obj):判断当前map和参数对象obj是否相等 元视图操作类: Set keySet():返回所有key构成的Set集合 Collection values():返回所有value构成的Collection集合 Set entrySet():返回所有key-value对构成的Set集合
3、底层原理实现
-
ArrayList
创建集合对象时,初始化大小都为10,对于JDK7,一创建集合对象就创建了底层数组,是饿汉式,而对于 JDK8,只有首次使用add方法时才创建底层数组,是懒汉式。 数组需要扩容时,当继续添加元素导致数组长度不够时就进行扩容,扩容为原来的1.5倍
-
HashSet:数据 + 链表
我们向集合中添加元素a,首先调用元素a的hashcode方法,计算其哈希值,然后根据哈希值,通过某种算法计算出a在数组中的索引位置
-
判断此位置上是否有数,若有数b,则比较a和b的哈希值
-
若相同,则调用a的equals方法,
-
若返回true,则认为两个元素是一样的,添加失败;
-
-
-
若添加成功,则他们在数组的该索引位置上以链表的方式存在
-
七上八下:对JDK7,新加的元素a上方(接近数组索引),原来的依次后移;而对于JDK8,新加的元素a在链表的末尾。
-
-
-
HashMap:与HashSet基本差不多,只是多了扩容机制和红黑树的问题。八股文必考,这里不再深究。
6、泛型
看得懂,就行。
7、Java反射机制
难啊!看不懂啊!幸亏用的不多,几乎不用自己实现(虽然框架底层源码很多用到了反射)。
8、Java8新特性
1、 Lambda 表达式(非常重要,多处用到)
在Java 8 语言中引入了一种新的语法元素和操作符 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的参数列表
右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。
它有六种语法格式如下:
//语法格式一:无参,无返回值 String r1 = ()->{System.out.println("Lambda!");} //语法格式二:Lambda 需要一个参数,但是没有返回值。 String r1 = (String str)->{System.out.println(str);} //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” String r1 = (str)->{System.out.println(str);} //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略 String r1 = str->{System.out.println(str);} // 语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 String r1 = str->{System.out.println(str);return str;} //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略 String r1 = (str)->{System.out.println("Lambda!");}