下标从0开始–length-1结束
dataType[] A;///数组类型[] 数组名
dataType[] A = new dataType[数组长度];
数组的元素是通过索引访问的,数组索引从0开始。
int[] a ={1,2,3,4,5};
int[] b =new int[2]; b[0]=1; b[1]=2;
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
数组下标越界异常报错: ArrayIndexOutofBounds
数组作为方法的参数
数组作返回值
int a[行][列] = new int a[2][3];
toString()–打印数组元素
sort()–升序排序
冒泡排序无疑是最为出名的排序算法之一, 总共有八大排序!
冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较。
我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为0(n2)。
import java.util.Arrays; //1. 比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置 //2. 每一次比较,都会产生出一个最大,或者最小的数字; //3. 下一轮则可以少一次排序! //4. 依次循环,直到结束! public class Demo11 { public static void main(String[] args) { int[] a = {1, 4, 5, 72, 2, 25, 6, 7}; int[] sort = sort(a); //调用完我们自己写的排序方法以后,返回一个排序后的数组 System.out.println(Arrays.toString(sort)); } public static int[] sort(int[] array) { //临时变量 int temp = 0; //外层循环,判断我们这个要走多少次; for (int i = 0; i < array.length - 1; i++) { Boolean flag = false; //内层循环,比价判断两个数,如果第一个数,比第二个数大,则交换位置 for (int j = 0; j < array.length - 1 - i; j++) { if (array[j + 1] > array[j]) { temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; flag = true; } } if (flag == false){ break; } } return array; } }
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
抽象
三大特性:
class 类名{//类体 成员变量 成员方法 }
类名 对象名 = new 类名();
该露的露,该藏的藏
我们程序设计要追求**“高内聚,低耦合”**。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
封装(数据的隐藏)
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。、
记住这句话就够了:属性私有,get/set
//类中只有属性和方法 public class Student { //类 private: 私有 //属性私有 private String name; //名字 private int id; //学号 private char sex; //性别 private int age; //提供一些可以操作这个属性的方法! //提供——public的get. set方法 //get获得这个数据 public String getName(){ return this.name; } //set给这个数据设置值 public void setName(String name){ this.name = name; } //alt + insert public int getAge() { return age; } public void setAge(int age) { if(age <0 || age > 120){ this.age = 3; }else { this.age = age; } } public int getId() { return id; } public void setId(int id) { this.id = id; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } }
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
extends的意思是“扩展”。子类是父类的扩展。
JAVA中类只有单继承,没有多继承!–一个儿子只能有一个爸爸,一个爸爸可以有多个儿子。
被final修饰的类不能被继承。
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
class 子类名 extends 父类名{ 类体//新添加的新数据域和方法 }
java中的所有类都直接或间接继承Object类。
//重写都是方法的重写,和属性无关 public class Demo04_B { public static void test(){ System.out.println("B==>test()"); } }
public class Demo04_A extends Demo04_B{ public static void test(){ System.out.println("A==>test()"); } }
ppublic class Applications { public static void main(String[] args) { //Demo04 //方法的调用只和左边,定义的数据类型有关 Demo04_A a = new Demo04_A(); a.test();//A==>test() //父类引用指向了子类 Demo04_B b = new Demo04_A(); b.test();//B==>test() } }
2.父类非静态方法
//重写都是方法的重写,和属性无关 public class Demo04_B { public void test(){ System.out.println("B==>test()"); } }
public class Demo04_A extends Demo04_B{ //Override 重写 @Override //注解:有功能的注释! public void test() { System.out.println("A==>test()"); } }
public class Applications { public static void main(String[] args) { //Demo04 /* 静态方法和非静态方法区别很大 静态方法:方法的调用只和左边,定义的数据类型有关 非静态方法:重写,父类方法只能是public */ Demo04_A a = new Demo04_A(); a.test();//A==>test() //父类引用指向了子类 Demo04_B b = new Demo04_A();//子类重写了父类的方法 b.test();//A==>test() } }
public class Person { public void run(){ System.out.println("Person==>run()"); } }
public class Student extends Person{ @Override public void run() { System.out.println("Student==>run()"); } public void stop(){ System.out.println("Student==>stop()"); } }
public class Applications { public static void main(String[] args) { // 一个对象的实际类型是确定的:new Student();new Person(); //可以指向的引用类型就不确定了:父类的引用指向子类 //Student 能调用的方法都是自己的或者继承父类的! Student s1 = new Student(); //Person 父类型,可以指向子类,但是不能调用子类独有的方法 Person s2 = new Student(); Object s3 = new Student(); //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大! s2.run(); //子类重写了父类的方法。执行子类的方法 Student==>run() s1.run();//Student==>run() s1.stop();//Student==>stop() Person p1 = new Person(); p1.run();//Person==>run() } }
多态注意事项:
public class Applications { public static void main(String[] args) { Object object = new Student(); //System. out . println(X instanceof Y);//能不能编译通过! X,Y之间要存在父子关系 //System. out . println(X instanceof Y);// X是Y之的子类,true System.out.println(object instanceof Student); //true System.out.println(object instanceof Person); //true System.out.println(object instanceof Object); //true System.out.println(object instanceof Teacher); //False System.out.println(object instanceof String); //False System.out.println("==========================="); Person person = new Student(); System.out.println(person instanceof Student); //true System.out.println(person instanceof Person); //true System.out.println(person instanceof Object); //true System.out.println(person instanceof Teacher); //False //System.out.println(person instanceof String); //不能通过编译 System.out.println("==========================="); Student student = new Student(); System.out.println(student instanceof Student); //true System.out.println(student instanceof Person); //true System.out.println(student instanceof Object); //true //System.out.println(student instanceof Teacher); //不能通过编译 //System.out.println(student instanceof String); //不能通过编译 } }
转换
public class Application { public static void main(String[] args) { //子类转换为父类,可能丢失自己的本来的一些方法! Student student = new Student();//自动转换,此时不能通过父类变量去调用子类的某些方法。 student.go(); Person person =new student(); } }
public class Application { public static void main(String[] args) { Person obj = new Student(); ((Student) obj).go();//强制转换 } }
非静态可以调用静态,但是静态不能调用非静态。
~~~java public class Student { //2:赋初值~ { System.out.println("匿名代码块"); } //1 :只执行一次~ static { System.out.println("静态代码块");//先执行静态,静态伴随着类出生 } //3 public Student() { System.out.println("构造方法"); } public static void main(String[] args) { Student student1 = new Student(); System.out.println("=========="); Student student2 = new Student(); } } ~~~ ~~~java //静态导入包~ import static java.lang.Math.random; import static java.lang.Math.PI; public class demo01 { public static void main(String[] args) { System.out.println(random()); System.out.println(PI); } } ~~~
//约束有人帮我们实现~ public abstract class Action { //abstract, 抽象方法,只有方法名字,没有方法的实现! public abstract void run(); public void go(){ System.out.println("Action==>go()"); } //1. 不能new这个抽象类,只能靠子类去实现它;约束! //2. 抽象类中可以写普通的方法~ //3. 抽象方法必须在抽象类中~ }
public class A extends Action{ //必须实现父类的抽象方法,除非其本身也是抽象类,就让下一个子类来实现抽象方法。 @Override public void run() { System.out.println("A==>run()"); } }
普通类:只有具体实现
抽象类:具体实现和**规范(抽象方法)**都有!
接口:只有规范! 自己无法写方法~专业的约束!约束和实现分离:面向接口编程
接口就是规范,定义的是一组规则,体现了现实世界中“如果你…则必须能.的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
public interface UserService { //interface定义的关键字,接口都需要有实现类 //常量~ public static final,一般不在接口中定义常量 int AGE = 99; //按口中的所有定义的方法其实都是抽象的public abstract void add(String name); void delete(String name); void update(String name); void query(String name); } public interface TimeService { void timer(); } //抽象类: extends~ //类可以实现接口implements 接口 //实现了接口的类,就需要重写接口中的方法~ //多继承~利用接口实现多继承 public class UserServiceImpl implements UserService, TimeService { @Override public void add(String name) { } @Override public void delete(String name) { } @Override public void update(String name) { } @Override public void query(String name) { } @Override public void timer() { } }
内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
public class Outer { private int age = 10; public void out(){ System.out.println("这是外部类的方法"); } public class Inner{ public void in(){ System.out.println("这是内部类的方法"); } public void getAge(){ System.out.println(age);//内部类获得外部类的私有属性获得方法 } } }
public class Applications { public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();//在外部访问内部类 inner.getAge(); inner.in(); outer.out(); } }
public class Outer { private int age = 10; public void out(){ System.out.println("这是外部类的方法"); } public static class Inner{//加上static就是静态内部类 public void in(){ System.out.println("这是内部类的方法"); } public void getAge(){ System.out.println(age);//内部类获得外部类的私有属性获得方法 } } }
public void method(){//在方法中创建类 class Inner1{ public void in1(){ } } }
new App()//匿名//没有名字初始化类,不用将实例保存到变量中。
实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序再跑着,内存或硬盘可能满了。等等。
软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理。而不至于程序崩溃。
异常指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。
异常发生在程序运行期间,它影响了正常的程序执行流程。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
异常体系结构
Error
Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
Java虚拟机运行错误(Virtual MachineError) ,当JVM不再有继续执行操作所需的内存资源
时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM) 一般会选择线程终
止;
还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError) 、链接错误
(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且
绝大多数是程序运行时不允许出现的状况。
Exception
Error和Exception的区别:
Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程; Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
抛出异常 捕获异常 异常处理五个关键字 try、catch、finally、throw、throws
public class Demo1 { public static void main(String[] args) { int a=1; int b=0; //假设要捕获多个异常:从小到大! try { //try监控区域 System.out.println(a/b); } catch (Error e){ //catch(想要捕获的异常类型! )捕获异常 System.out.println("Error"); } catch (Exception e){ System.out.println( "Exception"); } catch (Throwable t){ System.out.println("Throwable"); } finally { //处理善后工作//无论是否捕获到异常,总要执行finally后面的语句块。 System.out.println( "finally"); } //finally不是必须的部分,,finally,假设IO, 资源,关闭! }
public class Demo02 { public static void main(String[] args) { try { new Demo02().test( 1, 0); } catch (ArithmeticException e) { e. printStackTrace(); } } //假设这方法中,处理不了这个异常。方法上抛出异常 public void test(int a,int b) throws ArithmeticException { if (b == 0) { //throw throw new ArithmeticException(); //主动的抛出异常,把异常处理掉,不执行e.printStackTrace(),一般在方法中使用。 } System.out.println(a/b); } }
public class MyException extends Exception{ //自定义的异常类 private int detail; public MyException(int a){ this.detail = a; } @Override public String toString() {//异常的打印信息 return "MyException{"+detail+"}"; } }
public class Test { //可能会存在异常的方法通过throws声明 static void test(int a) throws MyException{ System.out.println("details = "+a); if (a>10){ throw new MyException(a);//抛出 } System.out.println("ok"); } public static void main(String[] args) { try { test(11); } catch (MyException e) { //增加一些处理异常的代码块 System.out.println("MyException-->"+e); } finally { } } }
程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期。如:运行中的QQ,运行中的MP3播放器程序是静态的,进程是动态的。进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
多线程程序的优点:
提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
提高计算机系统CPU的利用率
改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
何时需要多线程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lcIiBbtM-1621605959485)(G:\桌面\截图\QQ截图20210517190429.png)]
//多线程的创建,方式一:继承Thread类 //1.创建一个继承于Thread类的子类 //2.重写Thread类中的run()-->线程要执行的操作写在run()中 //3.创建Thread类的子类的对象 //4.通过此对象调用start() public class MyThread extends Thread{//Thread的子类实现线程 public void run(){//重写了run()方法,定义线程的任务 for (int i = 0; i <= 100; i++) { if (i%2==0){ System.out.println(i); } } } public static void main(String[] args) { MyThread tp1 = new MyThread();//线程的创建 tp1.start();//线程的启动 //问题1:不能直接调用run()的方式,此时只是调用run方法,并没有启动线程 //tp1.run(); //问题2:在启动一个线程,遍历100以内的偶数,不可以还让已经start()的线程去执行。会报IllegalThreadStateException错误 //我们需要重新创建一个线程的对象 MyThread tp2 = new MyThread(); tp2.start(); //如下操作仍然是在主线程中执行的 for (int i = 0; i < 100; i++) { if (i%2!=0){ System.out.println(i+"*****main()******"); } } } }
//创建多线程的方式二:实现Runnable接口 //1.创建一个实现了Runnable接口的类 //2.实现类去实现Runnable中的抽象方法:run() //3.创建实现类的对象 //4.将此对象作为参数传递到Thread类的构造器中,创造Thread类的对象 //5.通过Thread类的对象调用start() class Mthread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i%2==0){ System.out.println(i); } } } } public class Run { public static void main(String[] args) { Thread thread = new Thread(new Mthread());//==Mthread mthread = new Mthread();Thread thread = new Thread(mthread) thread.start(); } }
例:
Thread类常用方法:
优先选择实现Runnable接口的方式创建线程
一个完整的生命周期中通常要经历如下的五种状态:
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建
状态。
**就绪:**处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已
具备了运行的条件,只是没分配到CPU资源。
**运行:**当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线
程的操作和功能。
**阻塞:**在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中
止自己的执行,进入阻塞状态。
**死亡:**线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tpUbdgIk-1621605959487)(G:\桌面\截图\QQ截图20210518195408.png)]
/** 1.问题:卖票中,出现重票,错票-->出现了线程安全问题 2.问题出现原因:当某个线程操作车票过程中, 尚未操作完成时,其他线程参与进来,也来操作车票。 3.如何解决:当一个线程a操作ticket的时候,其他线程不能参与进来,直到线程a操作完ticket时,其他线程才可以开始操作ticket 这种情况即使线程a出现了阻塞,也不能被改变。 4.Java中,我们通过同步机制,来解决线程的安全问题。 方式一: 同步代码块--锁代码块 synchronized(同步监视器){ 需要被同步的代码 } 说明:1.操作共享数据的代码,即为需要被同步的代码--不能包含代码多了,也不能包含代码少了。 2.共享数据:多个线程共同操作的变量,比如这里的ticket就是共享数据。 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。 要求:多个线程必须要共用同一把锁。 方式二:同步方法--锁方法 1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明 2.非静态的同步方法,同步监视器是:this 静态的同步方法,同步监视器是:当前类本身 方式三:Lock锁 5.同步的方式:解决了线程的安全问题。 操作同步代码块时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低。 */
//方式一解决Runnable接口的线程安全问题 class window2 implements Runnable { private int ticket = 100; //Object obj = new Object(); //或者 @Override public void run() { while (true){ synchronized (this) {//此时的this:唯一的window2的对象w //或者:synchronized (obj) if (ticket > 0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName() + "卖票: 票号为:" + ticket); ticket--; } else { break; } } } } } public class Run3 { public static void main(String[] args) { window2 w = new window2(); //如果这里的window2创建了多个对象,同步监视器就不能使用this,锁不唯一,而且此时的线程对象不能共用ticket,必须加static才能共用一个ticket。 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(); } }
//方式一解决继承Thread的线程安全问题 class window1 extends Thread{ static private int ticket = 100; static Object obj = new Object(); //要共用同一把锁,此时创建锁对象要加static,因为window1创建了三个对象,此时的锁就不是同一把锁,加了static后才是同把锁。 @Override public void run() { while (true) { //或者synchronized(window1.class)---//Class class = window1.class,window1.class只会加载一次 synchronized (obj) {//不能使用synchronized(this):这里的this代表着t1,t2,t3三个对象,锁就不唯一 if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖票: 票号为:" + ticket); ticket--; } else { break; } } } } } public class Run2 { public static void main(String[] args) { window1 t1 = new window1(); window1 t2 = new window1(); window1 t3 = new window1(); t1.setName("窗口1:"); t2.setName("窗口2:"); t3.setName("窗口3:"); t1.start(); t2.start(); t3.start(); } }
//方法二使用同步方法解决实现Runnable接口的线程安全问题 class window3 implements Runnable { private int ticket = 100; @Override public void run() { while (true) {//循环调用show(),不循环每个线程都只执行一次 show(); } } private synchronized void show(){//同步监视器:this if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖票: 票号为:" + ticket); ticket--; } } } public class Run4 { public static void main(String[] args) { window3 w = new window3(); 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(); } }
//方法二使用同步方法解决继承Thread的线程安全问题 class window4 extends Thread{ static private int ticket = 100; @Override public void run() { while (true) { show(); } } private static synchronized void show(){//同步监视器:window4.class //private synchronized void show() 同步监视器:t1,t2,t3,此种解决方式是错误的 if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖票: 票号为:" + ticket); ticket--; } } } public class YouThread { public static void main(String[] args) { window4 t1 = new window4(); window4 t2 = new window4(); window4 t3 = new window4(); t1.setName("窗口1:"); t2.setName("窗口2:"); t3.setName("窗口3:"); t1.start(); t2.start(); t3.start(); } }
//解决线程安全问题的方式三:Lock锁 //Lock其实是一个接口,ReentrantLock是Lock的一个实现类 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class window5 implements Runnable{ private int ticket = 100; // 1.实例化ReentrantLock private ReentrantLock lock = new ReentrantLock();//括号中写true时,为公平,不写为false。 @Override public void run() { while (true){ try { //2.调用锁定方法:lock(); lock.lock(); if (ticket > 0){ try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } finally { } System.out.println(Thread.currentThread().getName() + "售票:票号为:" + ticket); ticket--; }else { break; } } finally { // 3.必须调用解锁方法:unlock(); lock.unlock(); } } } } public class LockTest { public static void main(String[] args) { window5 w = new window5(); 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只有代码块锁,synchronized有代码块锁和方法锁。
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有
更好的扩展性(提供更多的子类)。
优先使用顺序:
Lock -->同步代码块(已经进入了方法体,分配了相应资源)–>同步方法
(在方法体之外)
死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃
己需要的同步资源,就形成了线程的死锁,出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于
阻塞状态,无法继续。
解决方法:
专门的算法、原则
尽量减少同步资源的定义
尽量避免嵌套同步
//死锁的演示 public class DeadLockTest { public static void main(String[] args) { final StringBuffer s1 = new StringBuffer(); final StringBuffer s2 = new StringBuffer(); new Thread() { public void run() { synchronized (s1) { s2.append("A"); try{ Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace }//休眠后准备使用s2,但是此时的s2下面正在使用。线程分别占用对方需要的同步资源不放弃,都在等待对方放弃 //自己需要的同步资源,就形成了线程的死锁 synchronized (s2) { s2.append("B"); System.out.print(s1); System.out.print(s2); } } } }.start(); new Thread() { public void run() { synchronized (s2) { s2.append("C"); try{ Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace }//休眠后准备使用s1,但是此时的s1上面正在使用。 synchronized (s1) { s1.append("D"); System.out.print(s2); System.out.print(s1); } } } }.start(); } }
//线程通信例子:使用两个线程打印 1-100。线程1, 线程2 交替打印 //三个方法: //1.wait():使当前线程进入阻塞状态,并释放锁。 //2.notify():唤醒被wait()的一个线程,如果有多个线程被wait(),就会唤醒优先级高的那个。 //3.notifyAll():唤醒所有被wait()的线程。 // //注意: //1.这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常。 //2.因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在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) { System.out.println(Thread.currentThread().getName() + ":" + number); number++; try { //使得调用如下wait()方法的线程进入阻塞状态,wait()会释放锁,而sleep()不会释放锁 wait(); } catch (InterruptedException e) { e.printStackTrace(); } finally { } } } } } } public class CommunicationTest { public static void main(String[] args) { Number n = new Number(); Thread t1 = new Thread(n); Thread t2 = new Thread(n); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } }
/** * 例题:生产者、消费者问题 * 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图 * 生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通 * 知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如 * 果店中有产品了再通知消费者来取走产品。 */ class Clerk { private int productcount = 0; //生产产品 public synchronized void productorProduct() { if (productcount < 20) { productcount++; System.out.println(Thread.currentThread().getName() + ":开始生产第" + productcount); notify(); } else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } //消费产品 public synchronized void customerProduct() { if (productcount > 0) { System.out.println(Thread.currentThread().getName() + ":开始消费第" + productcount); productcount--; notify(); } else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Productor extends Thread{ private Clerk clerk; public Productor(Clerk clerk){ this.clerk = clerk; } @Override public void run() { System.out.println(getName() + ":开始生产产品"); while(true){ try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } clerk.productorProduct(); } } } class Customer extends Thread{ private Clerk clerk; public Customer(Clerk clerk){ this.clerk = clerk; } @Override public void run() { System.out.println(getName() + ":开始消费产品"); while (true){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } clerk.customerProduct(); } } } public class ProductorTest { public static void main(String[] args) { Clerk clerk = new Clerk(); Productor p = new Productor(clerk); p.setName("生产者"); p.start(); Customer c = new Customer(clerk); c.setName("消费者"); c.start(); } }
Annotation是从JDK5.0开始引入的新技术).
Annotation的作用 :
Annotation的格式:
注解是以"@注释名"在代码中存在的, 还可以添加一些参数值,例如:@SuppressWarnings(value=“unchecked”).
Annotation在哪里使用?
可以附加在package , class , method , field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
重写方法的注解:@Override : 定义在java.lang.Override中,此注释只适用于修辞方法, 表示一个方法声明打算重写超类中的另一个方法声明.
不推荐使用此方法的注解:@Deprecated :定义在java.lang.Deprecated中,此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择.
抑制警告的注解:@SuppressWarnings : 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息,与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了.
@SuppressWarnings(“all”)
@SuppressWarnings(“unchecked”)
@SuppressWarnings(value={“unchecked”,“deprecation”})
1. 元注解的**作用就是负责注解其他注解**, Java定义了4个标准的meta -annotation类型,他们被用来提供对其他annotation类型作说明. 2. 这些类型和它们所支持的类在java.lang.annotation包中可以找到.( @ Target , @Retention,@Documented , @Inherited ) - @Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方) - @Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期 - (SOURCE < CLASS < RUNTIME) - @Document:说明该注解将被包含在javadoc中 - @Inherited: 说明子类可以继承父类中的该注解
@MyAnnotation public class Demo02 { void test(){ } } //定义一个注解 //Target 表示我们的注解可以用在哪些地方. @Target(value = {ElementType.METHOD, ElementType.TYPE}) //Retention表示我们的注解在什么地方还有效。 // runtime>class>sources @Retention(value = RetentionPolicy.RUNTIME) //Documented表示是否将我们的注解生成在Javadoc中 @Documented //Inherited子类可以继承父类的注解 @Inherited @interface MyAnnotation{ }
使用@interface自定义注解时,自动继承了java.lang .annotation.Annotation接口
@ interface用来声明一个注解,格式: @ interface注解名{定义内容}
其中的每一个方法实际 上是声明了一个配置参数.
方法的名称就是参数的名称.
返回值类型就是参数的类型(返回值只能是基本类型,Class , String , enum ).
可以通过default来声明参数的默认值
如果只有一个参数成员, 一般参数名为value,此时value名还可以取消掉
注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值.
public class Demo03 { public static void main(String[] args) { } //注解可以显示赋值,有默认值时也可以赋值,如果没有默认值,必须给注解赋值 @MyAnnotation2(name = "小明",age = 18) public void test(){} @MyAnnotation3("小红")//参数只有一个,且参数名为value public void test1(){} } @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2{ //注解的参数:参数类型+参数名(); String name() default ""; int age() default 0; int id() default -1;// 如果默认值为-1,代表不存在。 String[] schools() default {"哈尔滨金融学院","哈尔滨工业大学"} } @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation3{ //只有一个参数时,参数名为value时,使用时不需参数名 String value(); }
在Object类中定义了以下的方法,此方法将被所有子类继承
public final Class getClass()
以上的方法返回值的类型是一 个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。-一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[)的有关信息。
获取Class类的对象
哪些类型可以有Class对象?
public class Demo03 { public static void main(String[] args) { Class c1 = Object.class; //类 . Class c2 = Comparable.class; //按口 Class c3 = String[].class; //- -维数组 Class c4 = int[][].class; //二维数组 Class c5 = Override.class; //注解 Class c6 = ElementType.class; //枚举 Class c7 = Integer.class; //基本数据类型 Class c8 = void.class; //void Class c9 = Class.class; //Class System.out.println(c1);//class java.lang.Object System.out.println(c2);//interface java.lang.Comparable System.out.println(c3);//class [Ljava.lang.String; System.out.println(c4);//class [[I System.out.println(c5);//interface java.lang.Override System.out.println(c6);//class java.lang.annotation.ElementType System.out.println(c7);//class java.lang.Integer System.out.println(c8);//void System.out.println(c9);//class java.lang.Class //只要元素类型与维度一样,就是同一个Class. int[] a = new int[10]; int[] b = new int[100]; System.out.println(a.getClass().hashCode());//1163157884 System.out.println(b.getClass().hashCode());//1163157884 } }
public class Demo04 { public static void main(String[] args) { A a=new A(); System.out.println(A.m);//100 /* 1.加载到内存,产生一个类对应Class对象 2.链接,链接结束后m=◎ 3.初始化 <clinit>(){ System. out . println( "A类静态代码块初始化") ; m = 300; m=100; } m=100 */ } } class A { static { System.out.println("A类静态代码块初始化"); m = 300; } static int m = 100; public A() { System.out.println("A类的参构造初始化"); } }
public class Demo05 { static { System.out.println("main类被加载"); } public static void main(String[] args) throws ClassNotFoundException { //1.主动引用 //Son son = new Son(); /* * main类被加载 * 父类被加载 * 子类被加载 * */ //反射也会产生主动引用 //Class.forName ("com.reflection.Son"); /* * main类被加载 * 父类被加载 * 子类被加载 * */ //不会产生类的引用的方法 //System.out.println(Son.b); /* * main类被加载 * 父类被加载 * 2 * */ //Son[] array = new Son[5]; /*main类被加载*/ System.out.println(Son.M) ; /* * main类被加载 * 1 * */ } } class Father { static int b = 2; static { System.out.println("父类被加载"); } } class Son extends Father { static { System.out.println("子类被加载"); } static int m = 100; static final int M = 1; }
public class Demo06 { public static void main(String[] args) throws ClassNotFoundException { //获取系统的类加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //获取系统类加载器的父类加载器-->扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@4554617c //获取扩展类加载器的父类加载器- ->根加载器(引导类加载器)(C/c++) ClassLoader grantparent = parent.getParent(); System.out.println(grantparent);//null //测试当前类是哪个加载器加载的 ClassLoader classLoader = Class.forName("com.reflection.Demo06").getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //测试JDK内置的类是谁加载的 ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader1);//null //如何获得系统类加载器可以加载的路径 System.out.println(System.getProperty("java.class.path")); /* * D:\Environment\java\jre\lib\charsets.jar; * D:\Environment\java\jre\lib\deploy.jar; * D:\Environment\java\jre\lib\ext\access-bridge-64.jar; * D:\Environment\java\jre\lib\ext\cldrdata.jar; * D:\Environment\java\jre\lib\ext\dnsns.jar; * D:\Environment\java\jre\lib\ext\jaccess.jar; * D:\Environment\java\jre\lib\ext\jfxrt.jar; * D:\Environment\java\jre\lib\ext\localedata.jar; * D:\Environment\java\jre\lib\ext\nashorn.jar; * D:\Environment\java\jre\lib\ext\sunec.jar; * D:\Environment\java\jre\lib\ext\sunjce_provider.jar; * D:\Environment\java\jre\lib\ext\sunmscapi.jar; * D:\Environment\java\jre\lib\ext\sunpkcs11.jar; * D:\Environment\java\jre\lib\ext\zipfs.jar; * D:\Environment\java\jre\lib\javaws.jar; * D:\Environment\java\jre\lib\jce.jar; * D:\Environment\java\jre\lib\jfr.jar; * D:\Environment\java\jre\lib\jfxswt.jar; * D:\Environment\java\jre\lib\jsse.jar; * D:\Environment\java\jre\lib\management-agent.jar; * D:\Environment\java\jre\lib\plugin.jar; * D:\Environment\java\jre\lib\resources.jar; * D:\Environment\java\jre\lib\rt.jar; * D:\project\java-design\out\production\design02; * D:\IDEA\IntelliJ IDEA 2020.1.1\lib\idea_rt.jar */ } }
双亲委派机制:如果自己定义和jdk同名的类,运行时虚拟机会在系统的类加载器中寻找,再去扩展类加载器中寻找,再去根加载器中寻找,如果存在同名的类,会使用根加载器中的类,而不使用自己定义的类
//获得类的信息 public class ClassDemo1 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class c1 = Class.forName("com.study.basis.Student"); //获得包名+类名 Student student = new Student(); Class c2 = student.getClass(); System.out.println(c2); //获得类的信息 System.out.println(c1.getName());//获得包名+类名 System.out.println(c1.getSimpleName());//获得类名 //获得类的属性 System.out.println("======================="); Field[] fields = c1.getFields();//获取pubilc属性 fields = c1.getDeclaredFields();//获取类的任何属性 for (Field field : fields) { System.out.println(field); } //获得指定属性的值 Field name = c1.getDeclaredField("name"); System.out.println(name); //获得类的方法 System.out.println("========================="); Method[] methods = c1.getMethods();//获得本类和父类的所有public方法 for (Method method : methods) { System.out.println("methods " + method); } System.out.println("========================="); Method[] decmethods = c1.getDeclaredMethods();//获得本类的所有方法 for (Method method : decmethods) { System.out.println("decmethods " + method); } //获得指定方法 //需要传参数的原因:存在重载,参数可找到指定的方法 System.out.println("========================="); Method getName = c1.getMethod("getName", null); Method getName = c1.getDeclaredMethods();("getName", null); Method setName = c1.getMethod("setName", String.class); Method setName = c1.getDeclaredMethods()("setName", String.class); System.out.println(getName); System.out.println(setName); //获得构造器 System.out.println("========================="); Constructor[] constructors = c1.getConstructors(); for (Constructor constructor : constructors) { System.out.println("getConstructors " + constructor); } System.out.println("========================="); Constructor[] constructors1 = c1.getDeclaredConstructors(); for (Constructor constructor : constructors1) { System.out.println("getDeclaredConstructors " + constructors1); } //获得指定的构造器 Constructor getDeclaredConstructor = c1.getDeclaredConstructor(String.class, int.class); System.out.println("指定构造器" + getDeclaredConstructor); } }
//动态创建对象,通过反射 public class ClassDemo2 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //获得Class对象 Class c1 = Class.forName("com.study.basis.Student"); //构造一个对象 // Student student = (Student)c1.newInstance();//本质是调用了类的无参构造 // System.out.println(student); //通过构造器创建对象 Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class); Student student2 = (Student)constructor.newInstance("小明",18,001); System.out.println(student2); //通过反射调用普通方法 Student student3 = (Student)c1.newInstance(); //通过反射获得一个方法 Method setName = c1.getMethod("setName", String.class); //invoke:激活的意思 //(对象,"方法的值") setName.invoke(student3,"小乐"); System.out.println(student3.getName()); //通过反射操作属性 Student student4 = (Student)c1.newInstance(); Field name = c1.getDeclaredField("name"); //不能直接操作私有属性,我们需要关闭程序的安全检测,属性或方法的setAccessible(true). name.setAccessible(true); name.set(student4,"小花"); System.out.println(student4.getName()); } }
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class Demo10 { public void test01 (Map<String,User> map, List<User> list){ System.out.println("test01"); } public Map<String, User> test02(){ System.out.println("test02"); return null; } public static void main(String[] args) throws NoSuchMethodException { Method method = Demo10.class.getMethod("test01",Map.class,List.class); Type[] genericParameterTypes= method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("参数范型"+genericParameterType); if (genericParameterType instanceof ParameterizedType){ Type[] actualTypeAnguments=((ParameterizedType)(ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeAngument : actualTypeAnguments) { System.out.println("实际参数范型"+actualTypeAngument); } } } Method method1 = Demo10.class.getMethod("test02",null); Type getGenericReturnType= method1.getGenericReturnType(); if (getGenericReturnType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) getGenericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println("返回值范型" + actualTypeArgument); } } } }
public class Demo11 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c1 = Class.forName("com.reflection.Student2"); //通过反射获取注解 Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation);//@com.reflection.Table(value=db_student) } //获得注解中的value值 Table table = (Table)c1.getAnnotation(Table.class); String value = table.value(); System.out.println(value);//db_student //获得类指定的注解 Field f = c1.getDeclaredField("name"); Filed annotation=f.getAnnotation(Filed.class); System.out.println(annotation.columnName());//db_name System.out.println(annotation.length());//3 System.out.println(annotation.type());//varchar } } @Table("db_student") class Student2{ @Filed(columnName = "db_id",type = "int",length = 10) private int id; @Filed(columnName = "db_age",type = "int",length = 10) private int age; @Filed(columnName = "db_name",type = "varchar",length = 3) private String name; public Student2(){} public Student2(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } //类名的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Table{ String value(); } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface Filed{ String columnName(); String type(); int length(); } @Filed(columnName = "db_name",type = "varchar",length = 3) private String name; public Student2(){} public Student2(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } //类名的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Table{ String value(); } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface Filed{ String columnName(); String type(); int length(); }