java学习笔记
一、java基础篇
1、final关键字
final:最终的
1、final可以修饰的结构:类、方法、变量。
2、final 修饰类:此类不能被其他类所继承。比如:String类,System,StringBuffer类
3、final修饰方法:表明此方法不能被重写。比如:Object类中getClass();
4、final修饰变量:此时的”变量“就成为一个常量。
4.1 final修饰属性:可以考虑赋值的位置有:显式初始化、代码块、构造器中。
4.2 final修饰局部变量。
static final:用来修饰属性:全局常量
2、static关键字
3、Exception异常
一、异常体系结构
/*自定义异常类
1、继承现有的异常结构:RuntimeException、Exception
2、提供全局常量:serialVersionUID
3、提供重载的构造器
java.lang.Throwable
|-------java.lang.Error:一般不编写针对性的代码进行处理。
|-------java.lang.Exception:可以进行异常的处理
|------编译时异常(checked)
|----IOException
|-----FileNOtFoundException
|----ClassNotFoundException
|------运行时异常(unchecked)
|------NullPointerException
|------ArrayIndexOutOfBoundsException
|------ClassCastException
|------NumberFormatException
|------InputMismatchException
|------ArithmeticException
一、异常的处理:抓抛模型
过程一、“拋”,程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常的对象。
并将此对象抛出
一旦抛出对象以后,其后的代码就不在执行
关于异常对象的产生,1、系统自动生成的异常对象
2、手动的生成一个异常对象,并抛出(throw)
过程二、“抓”:可以理解为异常的处理方式:1、try-catch-finally 2、throws
二、try-catch-finally的使用
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式1
}catch(异常类型3 变量名3){
//处理异常的方式1
}catch(异常类型4 变量名4){
//处理异常的方式1
}
.......
finally{
//一定会执行的代码
}
说明:
1、finally是可选的。
2、使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常的对象,根 据。对象的类型,去catch中进行匹配。
3、一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成就跳出当前的
try-catch结构(没有finally的情况)。继续执行其后的代码。
4、catch中的异常如果满足子父类关系,则要求子类一定声明在父类的上面,否则,报错。
5、常用的异常对象处理的方式:1、String getMessage();2、 e.printStackTrace();
6、在try结构中声明的变量,再处理Try结构后,就不能被调用
体会:使用try-catch-finally将一个编译时异常,使得程序在编译时就不在报错,但是运行时仍可能报错。
相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
7、try-catch-finally可以嵌套
体会二:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了,
针对编译时异常,我们说一定要考虑异常的处理。
异常处理的方式二:throws+异常类型
1、“throws + 异常类型”写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对像,此对象满足throws后异常类型时,就会被抛出。
2、体会:try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。
3、开发中如何选择使用try-catch—finally 还是使用throws?
3.1如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常
,必须使用try-catch-finally方式处理。
3.2执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,我们建议这几个方法使用throws的方式进行处理,
而执行的方法a可以考虑使用try—catch—finally方式进行处理。
try-catch-finally
* 1、finally是可选的
* 2、finally中声明的是一定会被执行的代码,即使catch中又出现了异常,try中又有return语句,catch中
* 有return语句等情况。
* 3、像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动进行资源的
* 释放,此时的资源的释放,就需要声明在finally中。
方法重写的规则之一,
* 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
4、类的成员
(5)、内部类
1、Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
*
* 2、内部类的分类:成员内部类VS局部内部类(方法内、代码块类、构造器内)
* 3、成员内部类:
* 一方面,作为外部类的成员;
* {
* 调用外部类的结构
* 可以被static修饰
* 可以被四种不同的权限修饰
* }
* 另一方面,作为一个类;
* {
* 类内可以定义属性、方法、构造器等
* 可以继承、
* 可以被final修饰,表示此类不能被继承,否则可以被继承
* 可以被abstract修饰,表示此类不能被实例化。
* }
* 4、关注如下的3个问题
* 4.1如何实例化成员内部类的对象
* 4.2如何在成员内部类中区分调用外部类的结构
* 4.3开发中局部内部类的使用
public class InnerClassTest {
public static void main(String[] args) {
//创建一个dog的实例(静态的成员内部类):
Persons.Dog dog = new Persons.Dog();
dog.show();
//创建一个Bird的实例(非静态的成员内部类):
//Persons.Bird bird = new Persons.Dog();
Persons persons = new Persons();
Persons.Bird bird=persons.new Bird();
bird.sing();
bird.display("黄鹂");
}
}
class Persons{
String name="小明";
int age;
public void eat(){
System.out.println("人吃饭");
}
//静态成员内部类
static class Dog{
String name;
int age;
public void show(){
System.out.println("卡拉是条狗");
//eat();静态不能调用非静态
}
}
//非静态成员内部类
class Bird{
String name="杜鹃";
public Bird(){
}
public void sing(){
System.out.println("我是一只小小鸟");
Persons.this.eat();//调用外部类的非静态属性
System.out.println(age);
}
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类
System.out.println(Persons.this.name);//外部内
}
}
public void method(){
//局部内部类
class AA{
}
}
{
//局部内部类
class BB{
}
}
public Persons(){
//局部内部类
class CC{
}
}
}
5、mian方法的使用。
main()方法的使用说明,
1、main()方法作为程序的入口。
2、main()方法是普通的静态方法。
3、main()方法可以作为我们与控制台交互的方式。(之前,使用的是Scanner)
6、String方法测试
package com.javaStudy;
/**
* @author wanghao
* @date 2021/4/2 - 13:45
* String属于引用数据类型
* 声明String类型时使用一对""
* String可以和种数据类型进行运算 连接运算
*运算结果为String类型
*/
public class StringTest {
public static void main(String[] args) {
//练习:交换两个变量的值
int num1=10;
int num2=20;
System.out.println("num1 = "+ num1 + " num2 = "+num2);
//方式一:定义临时变量
/*int temp = num1;
num1=num2;
num2=temp;
System.out.println("num1 = "+ num1 + " num2 = "+num2);*/
//方式二:好处:不用定义临时变量 弊端:相加超出存储问题,有局限性。
/*num1 = num1 + num2;
num2=num1 - num2;
num1=num1-num2;
System.out.println("num1 = "+ num1 + " num2 = "+num2);*/
//方式三:使用位运算符
num1=num1^num2;
num2=num1^num2;
num1=num1^num2;
System.out.println("num1 = "+ num1 + " num2 = "+num2);
}
}
package com.javaStudy;
import org.junit.Test;
import javax.sound.midi.SoundbankResource;
/**
* @author wanghao
* @date 2021/4/11 - 12:00
*/
public class WrapperTest {
@Test
public void test1(){
int num1=10;
//System.out.println(num.toString());
Integer in1=new Integer(num1);
System.out.println(in1.toString());
Integer in2=new Integer("123");
System.out.println(in2.toString());
Float f1=new Float(12.3f);
Float f2=new Float(12.3);
System.out.println(f1);
System.out.println(f2.toString());
Boolean b1=new Boolean(true);
System.out.println(b1);
Boolean b2=new Boolean("123");
System.out.println(b2);
}
//包装类---》转换成基本数据类型
@Test
public void test2(){
Integer in1=new Integer(12);
int i1=in1.intValue();
System.out.println(in1+1);
}
@Test
public void test3(){
int num1=10;
method(num1);
Integer in1 =new Integer(num1);
int n2=in1;
}
public void method(Object obj){
System.out.println(obj);
}
@Test
public void test4(){
int num1=10;
//方式一
String str1=num1+"";
//方式二:调用String重载的valueOf(xxxx)
float f1=12.3f;
String str2=String.valueOf(f1);
Double d1=new Double(12.4);
String str3=String.valueOf(d1);
System.out.println(str3);
System.out.println(str2);
}
@Test
public void test5(){
String str1="123";
//String类型----》基本数据类型
/*错误的情况:
int num1=(int)str1;
Integer in1=(Integer)str1;
* */
int num2=Integer.parseInt(str1);
System.out.println(num2+1);
String str2="true1";
boolean b1=Boolean.parseBoolean(str2);
System.out.println(b1);
//Integer 内部定义了从-128~127范围的整数,如果使用自动装箱的方式直接使用 不用new 目的:提高效率
}
}
7、接口interface
package javaInterface;
/**
* @author wanghao
* @date 2021/4/14 - 16:11
*/
/*
* 接口定义:interface
* Java中,接口和类是并列的两个结构。
* JDK7以前,只能定义全局常量和抽象方法。
* >全局常量:public static final的
* >抽象方法:public abstract的
* JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
* >接口中定义的静态方法,只能通过接口来调用
* >通过实现类的对象,可以调用接口中的默认方法。
* >如果实现类重写了接口中的默认方法,调用时,仍然调用的实重写以后的方法。
* >如果子类(或实现类)继承的父类中的同名同参数的方法。那么子类在没有重写此方法的情况下,默认调用的是父类
* 中的同名同参数的方法(类优先原则)
* >如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
* 那么在实现类在没有重写此方法的情况下,报错。--->接口冲突。 需要重写此方法。
* 接口中不能定义构造器!意味着接口不可以被实例化
* Java中,接口通过让类去实现(implement)的方式来使用
* 如果实现类覆盖了接口中所有的抽象方法,则此实现类就可以实例化
* 如果实现类没有覆盖了接口中所有的抽象方法,则此实现类仍未一个抽象类
* 接口可以多继承
* 接口的具体使用,体现多态性
* 接口,实际上可以看做一种规范
* */
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
//Flyable.MIN_SPEED=2;
Flyable plane = new Plane();
plane.fly();
plane.stop();
Flyable kite = new Kite();
kite.fly();
kite.stop();
}
}
interface Flyable{
//全局常量
public static final int MAX_SPEED=7900;//第一宇宙速度
int MIN_SPEED=1;
//抽象方法
public abstract void fly();
//省略了public abstract
void stop();
}
interface Attackable{
void attack();
}
class Plane implements Flyable{
@Override
public void fly() {
System.out.println("通过引擎起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速停止");
}
}
class Kite implements Flyable{
@Override
public void fly() {
System.out.println("跑步起飞");
}
@Override
public void stop() {
System.out.println("拉线停止");
}
}
class Bullet implements Flyable,Attackable{
@Override
public void fly() {
}
@Override
public void stop() {
}
@Override
public void attack() {
}
}
8、线程
(1)线程的创建
多线程的创建,方式一:继承于Thread类
* 1、创建一个继承Thread类的子类
* 2、重写Thread类的run()----->将此线程执行的操作声明在run()中
* 3、创建Thread类的子类的对象
* 4、通过此对象调用start()
* 创建多线程的方式二:实现Runnable接口
* 1、创建一个实现了Runnable接口的类
* 2、实现类去实现Runnable中的抽象方法:run()
* 3、创建实现类的对象
* 4、将此对象作为参数到Thread类的构造器中,创建Thread类的对象
* 5、通过Thread类的对象调用start()
*
* 比较创建线程的两种方式:
* 1、开发中:优先选择:实现Runnable接口的方式
* 原因:1、实现的方式没有类的单继承性的局限性。
* 2、实现的方式更适合来处理多个线程有共享数据的情况。
*
* 联系:两种方式都需要重新写run(),将线程要执行的逻辑声明在run()中。
* 注意:wait()/notify()/notify() 是Object中的方法。
*
* 创建线程的方式三:实现Callable接口。————>JDK5.0新增
*1、创建一个实现Callable的实现类
* @author wanghao
* @date 2021/4/23 - 15:14
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
1、call()可以有返回值的。
2、call()可以抛出异常,被外面的操作捕获,获取异常的信息。
3、Callable是支持泛型的。
创建线程的方式四:使用线程池。
*好处:
* 1、提高响应速度(减少了创建线程的时间)
* 2、降低了资源消耗(重复利用线程池中线程,不需要每次都创建)
* 3、便于线程的管理
* corePoolSize:核心池的大小
* maximumPoolSize:最大线程数
* keepAliveTime:线程没有任务时最多保持多长时间后会终止。
//1、创建一个继承Thread类的子类
class MyThread extends Thread {
//重写Thread类的run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i%2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3、创建Thread类的子类的对象
MyThread myThread = new MyThread();
//4、通过此对象调用start():1、启动当前线程2、调用当前线程的run()方法
myThread.start();
//不能调用run()方法
//myThread.run();
//不能再次调用,可以再创建一个线程
MyThread t=new MyThread();
t.start();
//可以用匿名的方式
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
//1、创建一个实现了Runnable接口的类
class MThread implements Runnable{
//2、实现类去实现Runnable中的抽象方法:run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i%2 == 0) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
//3、创建实现类的对象
MThread mThread = new MThread();
Thread t1 = new Thread(mThread);
t1.setName("线程一:");
t1.start();//调用了Runnable类型的target的run();
Thread t2 = new Thread(mThread);
t2.setName("线程二:");
t2.start();
}
}
class NumThread implements Callable<Integer>{
//2、实现call方法,将此线程需要执行的操作声明在call()中。
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 1; i <=100 ; i++) {
if (i%2 == 0) {
System.out.println(i);
sum +=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3、创建Callable接口的实现类对象。
NumThread numThread = new NumThread();
//4、将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象。
FutureTask<Integer> futureTask = new FutureTask<Integer>(numThread);
//5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start();
new Thread(futureTask).start();
try {
//6、get()返回值即为FutureTask构造器参数Callable实现重写的call()的返回值。
Integer sum=futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
package javaStudyTop.Thread;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 创建线程的方式四:使用线程池。
*好处:
* 1、提高响应速度(减少了创建线程的时间)
* 2、降低了资源消耗(重复利用线程池中线程,不需要每次都创建)
* 3、便于线程的管理
* corePoolSize:核心池的大小
* maximumPoolSize:最大线程数
* keepAliveTime:线程没有任务时最多保持多长时间后会终止。
*
*
* @author wanghao
* @date 2021/4/23 - 20:10
*/
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if (i%2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if (i%2 != 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1、提供指定线程数量的线程池。
ExecutorService service = Executors.newFixedThreadPool(10);
// ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//设置线程池的属性
System.out.println(service.getClass());
//2、执行指定的线程的操作。需要提供Runnable或Callable接口实现类的对象。
service.execute(new NumberThread());//适合使用于Runnable
service.execute(new NumberThread1());
// service.submit(Callable callable);//适合使用于Callable
service.shutdown();
}
}
(2)、线程中的方法
测试Thread中的常用方法
* 1、start():启动当前线程,调用当前线程的run()
* 2、run():通常需要重写run方法,将创建的线程要执行的操作声明在此方法中
* 3、currentThread():静态方法,返回当前代码执行的线程
* 4、getName():获取当前线程的名字
* 5、setName();设置当前线程的名字
* 6、yield():释放当前CPU的执行权
* 7、join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
* 8、stop():已过时,当执行此方法时,强制结束当前线程。
* 9、sleep(Long millitime):让当前线程“睡眠"指定的millitime毫秒,在指定的millitime毫秒时间内,当前
* 线程是阻塞状态。
* 10、isAlive():判断当前线程是否存活。
*
* 线程的优先级:
* 1、
* MAX_PRIORITY:10
* MIN_PRIORITY:1
* NORM_PRIORITY:5--->默认的优先级
* 2、如何获取和设置当前线程的优先级:
* getPriority():获取线程的优先级
* SetPriority(int p):设置线程的优先级
* 说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上将,高优先级的线程高概率的情况下被执行。并不意味着只有当高
* 优先级的线程执行完以后,低优先级的线程才执行。
(3)、死锁的原因及解决方式
package javaStudyTop.Thread;
/**
* @author wanghao
* @date 2021/4/23 - 12:39
* 演示线程的死锁问题。
* 1、死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,
* 都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
*
* 2、说明:
* 1)、出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
* 2)、我们使用同步时,要避免出现死锁。
*
* 3、解决方法:
* 专门的算法、原则。尽量减少同步资源的定义。尽量避免嵌套同步。
*/
public class ThreadTest2 {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
(4)、进程通信
线程通信的例子:使用两个线程打印1-100.线程1和线程2交替打印
*
*涉及到的三个方法:
* wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步显示器。
* notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就幻想优先级高的那一个。
* notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
*
*
* 注意:三种方法只能使用在同步代码块或同步方法中。
* 调用者必须是,同步代码块或同步方法中的同步监视器。否则会出现异常。定义在Object中。
*
* 面试题:sleep()和wait()的异同?
* 1、相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
* 2、不同点:1)、声明的位置不同:Thread类中声明sleep(),Object类中声明wait();
* 2)、调用的要求不同:sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码块或同步方法中
* 3)、关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,
* wait会释放锁。
class Number implements Runnable{
private int number=1;
@Override
public void run() {
while (true){
synchronized (this) {
notify();
if(number<=100){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+number);
number++;
try {
//使得调用如下wait()方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程一");
t2.setName("线程二");
t1.start();
t2.start();
}
}
(5)、线程安全解决方式
* 例子:创建三个c窗口卖票,总票数为100张,使用实现Runnable接口的方式
* 存在线程安全问题,待解决。
* 1、问题:卖票过程中,出现了重票、错票--->出现了线程安全问题
* 2、问题出现的原因是什么:当某个线程操作车票过程中,尚未操作完成时,其他线程参与进来,也操作了车票。
* 3、如何解决:当一个线程在操作ticket的时候,其他线程不能把参与进来,直到线程a操作完ticket时,其他
* 线程才可以操作ticket,这种情况即使线程出现了阻塞,也不能被改变。
* 4、在JAVA中,我们通过同步机制,来解决线程的安全问题。
* 方式一:同步代码块
* synchronized(同步监视器){
* //需要被同步的代码,
* }
* 说明:1、操作共享数据的代码,即为需要被同步的代码------>不能包含代码多了,也不能包含代码少了。
* 2、共享数据:多个线程同步操作的变量。比如:ticket
* 3、同步监视器:俗称锁。任何一个类的对象,都可以充当锁。
* 要求:多个线程必须要公用同一把锁。
* 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器
* 方式二:同步方法
* 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。
*
*
* 关于同步方法的总结:
* 1、同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
* 2、非静态的同步方法,同步监视器是:this
* 3、静态的同步方法,同步监视器是:当前类本身。
*
*
*
*
*
*
*
*
* 5、同步的方式,解决了线程安全的问题。----->好处
* 操作同步代码时,只有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。----->局限性
*
*/
*说明:在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
class Window1 implements Runnable{
private int ticket = 100;
//Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowsTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
* 解决线程安全问题的方式三:Lock锁 ——————>JDK5.0新增
*1、面试题:synchronized 与 Lock的异同?
* 同:二者都可以解决线程安全问题。
* 异:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
* Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
* 使用顺序:
* Lock-->同步代码块-->同步方法
*
* 面试题:如何解决线程安全问题?有几种方式
* Lock-->同步代码块-->同步方法
*
class Window4 implements Runnable{
private int ticket = 100;
//1、实例化ReentranLock
private ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
while(true){
try {
//2、调用锁定的方法Lock()
lock.lock();
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"售票,票号为:"+ticket);
ticket--;
}else {
break;
}
} finally {
//3、调用解锁方法:unlock();
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window4 w = new Window4();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
(6)、线程通信的应用
经典例题:生产者/消费者问题
class Clerk{
//生产产品
private int productCount = 0;
public synchronized void produceProduct() {
if (productCount < 20) {
productCount++;
System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
notify();
}else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if (productCount > 0) {
System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
productCount--;
notify();
}else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{//生产者
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName() + ":开始生产产品.....");
while(true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{//消费者
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer p1 = new Producer(clerk);
p1.setName("生产者1");
Consumer c1 = new Consumer(clerk);
c1.setName("消费者1");
p1.start();
c1.start();
}
}
8、String类的理解
package com.wh.StringSy;
import org.junit.Test;
/**
* String的使用。
*
* @author wanghao
* @date 2021/4/23 - 20:43
*/
public class StringTest {
/*
* String的实例化方式:
* 方式一:通过字面量的方式
* 方式二:通过new+构造器的方式
*面试题:String s = new String("abc");方式创建对象,在内存中创建了几个对象?
* 两个:一个是堆空间new结构,另一个是char[]对应的常量池中的数据:“abc”
*
* !!!!只要其中有一个是变量,结果就在堆中。如果拼接的结果调用intern()方法,返回值就在常量池中。
*
*
*
* */
@Test
public void test2(){
//通过字面量的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2 = "javaEE";
//通过new+构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1==s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
}
/* String:字符串,使用一对“”引起来表示。
1、String声明为final,不可被继承
2、String实现了Serializable接口:表示字符串可以序列化的。
实现Comparable:表示String可以比较大小
3、String内部定义了final char[] value用于存储字符串数据。
4、String:代表不可变的字符序列。简称:不可变性。!!!!!!!!重新造
体现:1、当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
2、当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的。
3、当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域。
5、通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
6、字符串常量池中是不会存储相同类容的字符串的。
* */
@Test
public void test(){
String s1 = "abc";//字面量的定义方式。
String s2 = "abc";
s1="hello";
System.out.println(s1 == s2);
System.out.println(s1);//hello
System.out.println(s2);//abc
System.out.println("*********************************");
String s3 = "abc";
s3+="def";
System.out.println(s3);//abcdef
System.out.println(s2);
System.out.println("*********************************");
String s4="abc";
String s5 = s4.replace('a', 'm');
System.out.println(s4);
System.out.println(s5);
}
}
/*String、StringBuffer、StringBuilder三者的异同?
同:都使用底层char[]数组进行实现
String:不可变的字符序列:
StringBuffer:可变的字符序列:线程安全的,效率低。
StringBuilder:可变的字符序列:线程不安全,效率高。(JDK5.0新增)
源码分析:
String str1 = new String();new char[];
String str = new String(“ABC”);new char[]{'a','b','c'};
如果添加元素多于16,则需要扩容:增加一倍+2;将原有的元素复制到新的数组中。
指导意义:开发中建议使用:StringBuffer(int capacity)。
String str = new StringBuffer();
9、jdk 8 之前日期和时间的API测试
package com.wh.StringSy;
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
/**
* jdk 8 之前日期和时间的API测试
* 1、System类中的currentTimeMillis()
* 2、Data类 java.util.Date和
* 3、SimpleDateFormat
* 4、Calendar
* 两个构造器的使用
* 两个方法的使用
*
*
* java.sql.Data.
*
*jdk 8 之后日期和时间的API测试
*
*
*
*
* @author wanghao
* @date 2021/4/24 - 10:43
*/
public class DataTimeTest {
@Test
public void test(){
long time = System.currentTimeMillis();
//返回当前时间于1970年1,1,0,0,分之间以毫秒为单位的时间差。
//称为时间戳
System.out.println(time);
}
//两个操作:
// 3、SimpleDateFormat
@Test
public void testSimpleDateFormat(){
//实例化SimpleDateFormat:使用默认的构造器
SimpleDateFormat sdf = new SimpleDateFormat();
//格式化:日期——>字符串
Date date = new Date();
System.out.println(date);
String format = sdf.format(date);
System.out.println(format);
//解析:格式化的逆过程,字符串--->日期
String str="21-4-24 上午11:21";
try {
Date date1 = sdf.parse(str);
System.out.println(date1);
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("*********************");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
}
//练习一:字符串“2020-09-08”转化为java.sql.Date
@Test
public void testExer() throws ParseException {
String birth = "2020-09-08";
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd");
Date parse = sdf1.parse(birth);
// System.out.println(parse);
java.sql.Date birthDate = new java.sql.Date(parse.getTime());
System.out.println(birthDate);
}
//练习二:三天打鱼两天晒网。1990-01-01 xxxx-xx-xx 打鱼?晒网? 总天数%5==1 2 3 :打鱼 ?晒网。
//方式一:转毫秒数
//方式二:按照闰年来算
@Test
public void test4(){
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();//频率高
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
//of();//设定指定的年月日,没有偏移量///Local这几个类有不可变性。
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
System.out.println(localDateTime1);
System.out.println(localDate.getDayOfMonth());
}
}
9、Compare
package StringStudy.Compare;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
/**
* 一、说明:Java中的对象,正常情况下,只能比较:== 或 !=,不能使用 > 或<的
* 但是在开发的场景中,我们需要对多个对象进行排序。言外之意,就需要比较对象的大小。
* 如何实现?使用两个接口的任何一个:Comparable 或Comparator
*
* 二、Comparable接口的使用
*
*
*
* @author wanghao
* @date 2021/4/25 - 11:02
*/
public class CompareTest {
/* Comparable接口的使用举例:自然排序。
1、像String、包装类等实现了Comparable接口,重写了compareTo()方法,给出了比较两个对象大小的方式
2、像String、包装类重写compareTo()方法以后,进行了从小到大的排序。
3、重写compareTo()的规则:
如果当前对象this大于形参obj,则返回正整数,如果当前对象this小于形参,则返回负整数,相等返回0。
4、对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重新compareTo()方法。
在compareTo(obj)方法中指明如何排序。
* */
@Test
public void test1(){
String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
@Test
public void test2(){
Goods[] arr=new Goods[4];
arr[0]=new Goods("lenovoMouse",34);
arr[1]=new Goods("dellMouse",43);
arr[2]=new Goods("xiaomiMouse",12);
arr[3]=new Goods("huaweiMouse",65);
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
//2、Comparator:定制排序。
//重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
// 如果方法返回正整数,则表示o1小于o2。
//如果返回0,表示相等:
//返回负整数,表示o1小于o2。
@Test
public void test3(){
String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
System.out.println(Arrays.toString(arr));
}
@Test
public void test4(){
Goods[] arr=new Goods[5];
arr[0]=new Goods("lenovoMouse",34);
arr[1]=new Goods("dellMouse",43);
arr[2]=new Goods("xiaomiMouse",12);
arr[3]=new Goods("huaweiMouse",65);
arr[4]=new Goods("huaweiMouse",34);
Arrays.sort(arr, new Comparator<Goods>() {
@Override
public int compare(Goods o1, Goods o2) {
if(o1.getName().equals(o2.getName())){
return -Double.compare(o1.getPrice(),o2.getPrice());
}else{
return o1.getName().compareTo(o2.getName());
}
}
});
System.out.println(Arrays.toString(arr));
}
}
/* 二、 Comparable接口与Comparator的使用的对比:
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。
*
*
*
*
*
*
*
* */
10、集合
一、集合的概述
* 1、集合、数组都是对多个数据进行存储操作的结构,简称java容器。
* 说明:此时的存储,主要指的是内存层面的存储,不涉及持久化的存储
* 2.1数组在存储多个数据方面的特点:
* >一旦初始化后,其长度就确定了。
* >数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
* 2.2 数组在存储多个数据方面的缺点。
* >一旦初始化以后,其长度就不可以修改
* >数组中提供的方法非常有限,增删查改不方便,效率不高。
* >获取数组中实际元素的个数,无现成方法。
* >数组存储数据的特点:有序、可重复、对于无序,不可重复的需求,不能满足。
* 二、集合框架
* 1、Collection接口: 单列集合,用来存储一个一个的对象
* 1.1、List接口:存储有序的、可重复的数据。------>“动态数组”
* ArrayList、 LinkedList、vector
* 2.2、Set接口:存储无序的、不可重复的数据 ------->高中讲的集合。
* HashSet、LinkedHashSet、TreeSet
*
*
*
* 2、Map接口: 双列集合,用来存储一对(key,value)的数据。------>函数:y=f(x);
* HashMap、LinkedHashMap、TreeMap、Hashtable、Properties。
* 三、Collection接口中的方法的使用。
public class CollectionTest {
@Test
public void test1(){
Collection coll = new ArrayList();
/* //add(object) 将元素e加入。
coll.add("AAA");
coll.add("BBB");
coll.add("123");
coll.add(new Date());
//size() 添加元素的个数
System.out.println(coll.size());//4
//addAll()
Collection coll1=new ArrayList();
coll1.add(456);
coll1.add("CCC");
coll.addAll(coll1);
System.out.println(coll.size());//6
System.out.println(coll);
System.out.println(coll1);
//isEmpty:判断当前集合是否为空
System.out.println(coll.isEmpty());
coll.clear();//清空集合元素
System.out.println(coll.isEmpty());*/
System.out.println("************************************************");
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
// Person p = new Person("Jerry", 20);
// coll.add(p);
coll.add(new Person("Jerry", 20));
//contains(Object obj);判断当前集合中是否包含obj
//我们在判断时会调用obj对象所在类的equals();
boolean contains = coll.contains(123);
System.out.println(contains);
System.out.println(coll.contains(new String("Tom")));
//System.out.println(coll.contains(p));
System.out.println(coll.contains(new Person("Jerry", 20)));
}
}
二、设计模式
1、单例模式
package com.Singleton;
import com.sun.org.apache.xml.internal.res.XMLErrorResources_tr;
/**
* @author wanghao
* @date 2021/4/13 - 10:44
*/
/*
如:网站计数器、数据库连接池、Application
*单例设计模式:饿汉式!
* 1、所谓类的单例设计模式,就是采用一定的设计方法保证在整个软件的设计过程中,对某个类只能存在一个对象实例。
* 2、如何实现?
* 饿汉式VS懒汉式
* 3、区分饿汉式和懒汉式
*
* 饿汉式:坏处,对象加载时间过程。
* 好处:天然是对象安全的。
* 懒汉式:延迟对象的创建
* 目前的写法是线程不安全的。-->到多线程再修改
*
*
* */
public class SingletonTest1 {
public static void main(String[] args) {
Bank bank1=Bank.getInstance();
Bank bank2=Bank.getInstance();
System.out.println(bank1==bank2);
}
}
class Bank{
//1、私有化类的构造器
private Bank(){
}
//2、内部创建类的对象
//4、要求此对象也必须是静态的
private static Bank instance=new Bank();
//3、提供公共的静态的方法,返回类的对象。
public static Bank getInstance(){
return instance;
}
}
package com.interview;
/**
* @author wanghao
* @date 2021/3/26 - 16:22
*/
/**
*懒汉式(线程不安全)
*/
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){
}
public static SingletonDemo getInstance(){
if(instance == null){
instance = new SingletonDemo();
}
return instance;
}
}
/**
*懒汉式(线程安全-简单粗暴)
*/
class SingletonDemo02 {
private static SingletonDemo02 instance;
private SingletonDemo02(){
}
public static synchronized SingletonDemo02 getInstance(){
if(instance == null){
instance = new SingletonDemo02();
}
return instance;
}
}
/**
*饿汉式(线程安全)
*/
class SingletonDemo03 {
private static SingletonDemo03 instance = new SingletonDemo03();
private SingletonDemo03(){
}
public static SingletonDemo03 getInstance(){
return instance;
}
}
/**
*饿汉式(线程安全——静态内部类)
*/
class SingletonDemo04 {
private static class SingletonHolder{
private static SingletonDemo04 instance=new SingletonDemo04();
}
private SingletonDemo04(){
System.out.println("SingletonDemo04 has loaded");
}
public static SingletonDemo04 getInstance(){
return SingletonHolder.instance;
}
}
/**
*高级的枚举的(线程安全——静态内部类)
public enum SingletonDemo5{
INSTANCE;
public void doSomething(){
System.out.println("done!");
}
}
class Demo{
public static void main(String[] args) {
SingletonDemo5.INSTANCE;
}
}*/
/**
*饿汉式(线程安全——静态内部类)
class SingletonDemo04 {
private static class SingletonHolder{
private static SingletonDemo04 instance=new SingletonDemo04();
}
private SingletonDemo04(){
System.out.println("SingletonDemo04 has loaded");
}
public static SingletonDemo04 getInstance(){
return SingletonHolder.instance;
}
}*/
package com.interview;
/**
* @author wanghao
* @date 2021/3/26 - 16:53
* 懒汉式 (双重校验锁+volatile)
*/
public class Singleton {
private volatile static Singleton instance;
private Singleton(){
System.out.println("Singleton has loaded!");
}
public static Singleton getInstance(){
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2、责任链模式
package pattern;
/**
* @author wanghao
* @date 2021/4/15 - 17:01
*/
public class ChainRespPattern {
public static void main(String[] args) {
Handler level1 = new leader();
Handler level2 = new Boss();
level1.setNextHandler(level2);
level1.process(10);
level1.process(11);
}
}
abstract class Handler{
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler){
this.nextHandler=nextHandler;
}
public abstract void process(Integer info);
}
class leader extends Handler{
@Override
public void process(Integer info) {
if(info>0&&info<11)
System.out.println("Leader 处理!");
else
nextHandler.process(info);
}
}
class Boss extends Handler{
@Override
public void process(Integer info) {
System.out.println("Boss 处理!");
}
}
3、代理模式
package pattern;
/**
* @author wanghao
* @date 2021/4/16 - 19:25
*/
public class ProxyPattern {
public static void main(String[] args) {
Server server = new Server();
//ProxyServer proxyServer = new ProxyServer(server);
//proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work=work;
}
public void check(){
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
work.browse();
}
}
4、模板模式
package Template;
/**
* @author wanghao
* @date 2021/4/14 - 15:41
*/
public class TemplateTest {
public static void main(String[] args) {
Template template = new SubTemplate();
template.spendTime();
}
}
abstract class Template{
//计算某段代码执行所需要花费的时间
public void spendTime(){
long start = System.currentTimeMillis();
code();//不确定的部分,易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:"+(end-start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
for (int i = 2; i <=2000; i++) {
boolean isFlag=true;
for (int j = 2; j < Math.sqrt(i); j++) {
if(i%j==0){
isFlag=false;
break;
}
}
if(isFlag){
System.out.println(i);
}
}
}
}