-
同步和死锁:【问题引出】:比如说对于买票系统,有下面的代码:复制代码代码如下:/*** @author Rollen-Holt* */class hello implements Runnable {public void run() {for(int i=0;i<10;++i){if(count>0){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(count--);}}}public static void main(String[] args) {hello he=new hello();Thread h1=new Thread(he);Thread h2=new Thread(he);Thread h3=new Thread(he);h1.start();h2.start();h3.start();}private int count=5;}【运行结果】:543210-1这里出现了-1,显然这个是错的。,应该票数不能为负值。如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,其他的线程必须等到这个线程结束之后才能继续执行。【使用线程同步解决问题】采用同步的话,可以使用同步代码块和同步方法两种来完成。【同步代码块】:语法格式:synchronized(同步对象){//需要同步的代码}但是一般都把当前对象this作为同步对象。比如对于上面的买票的问题,如下:复制代码代码如下:/*** @author Rollen-Holt* */class hello implements Runnable {public void run() {for(int i=0;i<10;++i){synchronized (this) {if(count>0){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(count--);}}}}public static void main(String[] args) {hello he=new hello();Thread h1=new Thread(he);Thread h2=new Thread(he);Thread h3=new Thread(he);h1.start();h2.start();h3.start();}private int count=5;}【运行结果】:(每一秒输出一个结果)54321【同步方法】也可以采用同步方法。语法格式为synchronized 方法返回类型 方法名(参数列表){// 其他代码}现在,我们采用同步方法解决上面的问题。复制代码代码如下:/*** @author Rollen-Holt* */class hello implements Runnable {public void run() {for (int i = 0; i < 10; ++i) {sale();}}public synchronized void sale() {if (count > 0) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count--);}}public static void main(String[] args) {hello he = new hello();Thread h1 = new Thread(he);Thread h2 = new Thread(he);Thread h3 = new Thread(he);h1.start();h2.start();h3.start();}private int count = 5;}【运行结果】(每秒输出一个)54321提醒一下,当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。此处列举经典的生产者和消费者问题。【生产者和消费者问题】先看一段有问题的代码。复制代码代码如下:class Info {public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}private String name = "Rollen";private int age = 20;}/*** 生产者* */class Producer implements Runnable{private Info info=null;Producer(Info info){this.info=info;}public void run(){boolean flag=false;for(int i=0;i<25;++i){if(flag){this.info.setName("Rollen");try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}this.info.setAge(20);flag=false;}else{this.info.setName("chunGe");try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}this.info.setAge(100);flag=true;}}}}/*** 消费者类* */class Consumer implements Runnable{private Info info=null;public Consumer(Info info){this.info=info;}public void run(){for(int i=0;i<25;++i){try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}System.out.println(this.info.getName()+"<---->"+this.info.getAge());}}}/*** 测试类* */class hello{public static void main(String[] args) {Info info=new Info();Producer pro=new Producer(info);Consumer con=new Consumer(info);new Thread(pro).start();new Thread(con).start();}}【运行结果】:Rollen<---->100chunGe<---->20chunGe<---->100Rollen<---->100chunGe<---->20Rollen<---->100Rollen<---->100Rollen<---->100chunGe<---->20chunGe<---->20chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20大家可以从结果中看到,名字和年龄并没有对于。那么如何解决呢?<!--[if !supportLists]-->1) <!--[endif]-->加入同步<!--[if !supportLists]-->2) <!--[endif]-->加入等待和唤醒先来看看加入同步会是如何。复制代码代码如下:class Info {public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public synchronized void set(String name, int age){this.name=name;try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}this.age=age;}public synchronized void get(){try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}System.out.println(this.getName()+"<===>"+this.getAge());}private String name = "Rollen";private int age = 20;}/*** 生产者* */class Producer implements Runnable {private Info info = null;Producer(Info info) {this.info = info;}public void run() {boolean flag = false;for (int i = 0; i < 25; ++i) {if (flag) {this.info.set("Rollen", 20);flag = false;} else {this.info.set("ChunGe", 100);flag = true;}}}}/*** 消费者类* */class Consumer implements Runnable {private Info info = null;public Consumer(Info info) {this.info = info;}public void run() {for (int i = 0; i < 25; ++i) {try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}this.info.get();}}}/*** 测试类* */class hello {public static void main(String[] args) {Info info = new Info();Producer pro = new Producer(info);Consumer con = new Consumer(info);new Thread(pro).start();new Thread(con).start();}}【运行结果】:Rollen<===>20ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100Rollen<===>20ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100从运行结果来看,错乱的问题解决了,现在是Rollen 对应20,ChunGe对于100,但是还是出现了重复读取的问题,也肯定有重复覆盖的问题。如果想解决这个问题,就需要使用Object类帮忙了、,我们可以使用其中的等待和唤醒操作。
-
我们换为Runnable接口:复制代码代码如下:/*** @author Rollen-Holt 继承Thread类,不能资源共享* */class hello implements Runnable {public void run() {for (int i = 0; i < 7; i++) {if (count > 0) {System.out.println("count= " + count--);}}}public static void main(String[] args) {hello he=new hello();new Thread(he).start();}private int count = 5;}【运行结果】:count= 5count= 4count= 3count= 2count= 1总结一下吧:实现Runnable接口比继承Thread类所具有的优势:1):适合多个相同的程序代码的线程去处理同一个资源2):可以避免java中的单继承的限制3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。所以,本人建议大家劲量实现接口。复制代码代码如下:/*** @author Rollen-Holt* 取得线程的名称* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {System.out.println(Thread.currentThread().getName());}}public static void main(String[] args) {hello he = new hello();new Thread(he,"A").start();new Thread(he,"B").start();new Thread(he).start();}}【运行结果】:AAABThread-0Thread-0Thread-0说明如果我们没有指定名字的话,系统自动提供名字。提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。判断线程是否启动复制代码代码如下:/*** @author Rollen-Holt 判断线程是否启动* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {System.out.println(Thread.currentThread().getName());}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he);System.out.println("线程启动之前---》" + demo.isAlive());demo.start();System.out.println("线程启动之后---》" + demo.isAlive());}} 【运行结果】线程启动之前---》false线程启动之后---》trueThread-0Thread-0Thread-0主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。线程的强制执行:复制代码代码如下:/*** @author Rollen-Holt 线程的强制执行* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {System.out.println(Thread.currentThread().getName());}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he,"线程");demo.start();for(int i=0;i<50;++i){if(i>10){try{demo.join(); //强制执行demo}catch (Exception e) {e.printStackTrace();}}System.out.println("main 线程执行-->"+i);}}}【运行的结果】:main 线程执行-->0main 线程执行-->1main 线程执行-->2main 线程执行-->3main 线程执行-->4main 线程执行-->5main 线程执行-->6main 线程执行-->7main 线程执行-->8main 线程执行-->9main 线程执行-->10线程线程线程main 线程执行-->11main 线程执行-->12main 线程执行-->13...线程的休眠:复制代码代码如下:/*** @author Rollen-Holt 线程的休眠* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + i);}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he, "线程");demo.start();}}【运行结果】:(结果每隔2s输出一个)线程0线程1线程2线程的中断:复制代码代码如下:/*** @author Rollen-Holt 线程的中断* */class hello implements Runnable {public void run() {System.out.println("执行run方法");try {Thread.sleep(10000);System.out.println("线程完成休眠");} catch (Exception e) {System.out.println("休眠被打断");return; //返回到程序的调用处}System.out.println("线程正常终止");}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he, "线程");demo.start();try{Thread.sleep(2000);}catch (Exception e) {e.printStackTrace();}demo.interrupt(); //2s后中断线程}}【运行结果】:执行run方法休眠被打断在java程序中,只要前台有一个线程在运行,整个java程序进程不会小时,所以此时可以设置一个后台线程,这样即使java进程小时了,此后台线程依然能够继续运行。复制代码代码如下:/*** @author Rollen-Holt 后台线程* */class hello implements Runnable {public void run() {while (true) {System.out.println(Thread.currentThread().getName() + "在运行");}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he, "线程");demo.setDaemon(true);demo.start();}}虽然有一个死循环,但是程序还是可以执行完的。因为在死循环中的线程操作已经设置为后台运行了。线程的优先级:复制代码代码如下:/*** @author Rollen-Holt 线程的优先级* */class hello implements Runnable {public void run() {for(int i=0;i<5;++i){System.out.println(Thread.currentThread().getName()+"运行"+i);}}public static void main(String[] args) {Thread h1=new Thread(new hello(),"A");Thread h2=new Thread(new hello(),"B");Thread h3=new Thread(new hello(),"C");h1.setPriority(8);h2.setPriority(2);h3.setPriority(6);h1.start();h2.start();h3.start();}}【运行结果】:A运行0A运行1A运行2A运行3A运行4B运行0C运行0C运行1C运行2C运行3C运行4B运行1B运行2B运行3B运行4。但是请读者不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、另外,主线程的优先级是5.线程的礼让。在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。
-
java中的多线程在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。对于直接继承Thread的类来说,代码大致框架是:复制代码代码如下:class 类名 extends Thread{方法1;方法2;…public void run(){// other code…}属性1;属性2;…}先看一个简单的例子:复制代码代码如下:/*** @author Rollen-Holt 继承Thread类,直接调用run方法* */class hello extends Thread {public hello() {}public hello(String name) {this.name = name;}public void run() {for (int i = 0; i < 5; i++) {System.out.println(name + "运行 " + i);}}public static void main(String[] args) {hello h1=new hello("A");hello h2=new hello("B");h1.run();h2.run();}private String name;}【运行结果】:A运行 0A运行 1A运行 2A运行 3A运行 4B运行 0B运行 1B运行 2B运行 3B运行 4我们会发现这些都是顺序执行的,说明我们的调用方法不对,应该调用的是start()方法。当我们把上面的主函数修改为如下所示的时候:复制代码代码如下:public static void main(String[] args) {hello h1=new hello("A");hello h2=new hello("B");h1.start();h2.start();}然后运行程序,输出的可能的结果如下:A运行 0B运行 0B运行 1B运行 2B运行 3B运行 4A运行 1A运行 2A运行 3A运行 4因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。注意:虽然我们在这里调用的是start()方法,但是实际上调用的还是run()方法的主体。那么:为什么我们不能直接调用run()方法呢?我的理解是:线程的运行需要本地操作系统的支持。如果你查看start的源代码的时候,会发现:复制代码代码如下:public synchronized void start() {/*** This method is not invoked for the main method thread or "system"* group threads created/set up by the VM. Any new functionality added* to this method in the future may have to also be added to the VM.** A zero status value corresponds to state "NEW".*/if (threadStatus != 0 || this != me)throw new IllegalThreadStateException();group.add(this);start0();if (stopBeforeStart) {stop0(throwableFromStop);}}private native void start0();注意我用红色加粗的那一条语句,说明此处调用的是start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。通过实现Runnable接口:大致框架是:复制代码代码如下:class 类名 implements Runnable{方法1;方法2;…public void run(){// other code…}属性1;属性2;…}来先看一个小例子吧:复制代码代码如下:/*** @author Rollen-Holt 实现Runnable接口* */class hello implements Runnable {public hello() {}public hello(String name) {this.name = name;}public void run() {for (int i = 0; i < 5; i++) {System.out.println(name + "运行 " + i);}}public static void main(String[] args) {hello h1=new hello("线程A");Thread demo= new Thread(h1);hello h2=new hello("线程B");Thread demo1=new Thread(h2);demo.start();demo1.start();}private String name;}【可能的运行结果】:线程A运行 0线程B运行 0线程B运行 1线程B运行 2线程B运行 3线程B运行 4线程A运行 1线程A运行 2线程A运行 3线程A运行 4关于选择继承Thread还是实现Runnable接口?其实Thread也是实现Runnable接口的:复制代码代码如下:class Thread implements Runnable {//…public void run() {if (target != null) {target.run();}}}其实Thread中的run方法调用的是Runnable接口的run方法。不知道大家发现没有,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。关于代理模式,我曾经写过一个小例子呵呵,大家有兴趣的话可以看一下:http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144847.htmlThread和Runnable的区别:如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。复制代码代码如下:/*** @author Rollen-Holt 继承Thread类,不能资源共享* */class hello extends Thread {public void run() {for (int i = 0; i < 7; i++) {if (count > 0) {System.out.println("count= " + count--);}}}public static void main(String[] args) {hello h1 = new hello();hello h2 = new hello();hello h3 = new hello();h1.start();h2.start();h3.start();}private int count = 5;}【运行结果】:count= 5count= 4count= 3count= 2count= 1count= 5count= 4count= 3count= 2count= 1count= 5count= 4count= 3count= 2count= 1大家可以想象,如果这个是一个买票系统的话,如果count表示的是车票的数量的话,说明并没有实现资源的共享
-
JAVA源码编译由三个过程组成:1、源码编译机制。2、类加载机制3、类执行机制我们这里主要介绍编译和类加载这两种机制。一、源码编译代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码文件格式主要分为两部分:常量池和方法字节码。二、类加载类的生命周期是从被加载到虚拟机内存中开始,到卸载出内存结束。过程共有七个阶段,其中到初始化之前的都是属于类加载的部分加载----验证----准备----解析-----初始化----使用-----卸载系统可能在第一次使用某个类时加载该类,也可能采用预加载机制来加载某个类,当运行某个java程序时,会启动一个java虚拟机进程,两次运行的java程序处于两个不同的JVM进程中,两个jvm之间并不会共享数据。1、加载阶段这个流程中的加载是类加载机制中的一个阶段,这两个概念不要混淆,这个阶段需要完成的事情有:1)通过一个类的全限定名来获取定义此类的二进制字节流。2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。3)在java堆中生成一个代表这个类的Class对象,作为访问方法区中这些数据的入口。由于第一点没有指明从哪里获取以及怎样获取类的二进制字节流,所以这一块区域留给我开发者很大的发挥空间。这个我在后面的类加载器中在进行介绍。2、准备阶段这个阶段正式为类变量(被static修饰的变量)分配内存并设置类变量初始值,这个内存分配是发生在方法区中。1、注意这里并没有对实例变量进行内存分配,实例变量将会在对象实例化时随着对象一起分配在JAVA堆中。2、这里设置的初始值,通常是指数据类型的零值。private static int a = 3; 这个类变量a在准备阶段后的值是0,将3赋值给变量a是发生在初始化阶段。3、初始化阶段初始化是类加载机制的最后一步,这个时候才正真开始执行类中定义的JAVA程序代码。在前面准备阶段,类变量已经赋过一次系统要求的初始值,在初始化阶段最重要的事情就是对类变量进行初始化,关注的重点是父子类之间各类资源初始化的顺序。java类中对类变量指定初始值有两种方式:1、声明类变量时指定初始值;2、使用静态初始化块为类变量指定初始值。初始化的时机1)创建类实例的时候,分别有:1、使用new关键字创建实例;2、通过反射创建实例;3、通过反序列化方式创建实例。12new Test();Class.forName(“com.mengdd.Test”);2)调用某个类的类方法(静态方法)1Test.doSomething();3)访问某个类或接口的类变量,或为该类变量赋值。 12int b=Test.a;Test.a=b;4)初始化某个类的子类。当初始化子类的时候,该子类的所有父类都会被初始化。5)直接使用java.exe命令来运行某个主类。除了上面几种方式会自动初始化一个类,其他访问类的方式都称不会触发类的初始化,称为被动引用。1、子类引用父类的静态变量,不会导致子类初始化。12345678910111213141516171819202122public class SupClass{ public static int a = 123; static { System.out.println("supclass init"); }}public class SubClass extends SupClass{ static { System.out.println("subclass init"); }}public class Test{ public static void main(String[] args) { System.out.println(SubClass.a); }}执行结果:supclass init1232、通过数组定义引用类,不会触发此类的初始化123456789101112131415public class SupClass{ public static int a = 123; static { System.out.println("supclass init"); }}public class Test{ public static void main(String[] args) { SupClass[] spc = new SupClass[10]; }}执行结果:3、引用常量时,不会触发该类的初始化123456789101112131415public class ConstClass{ public static final String A= "MIGU"; static { System.out.println("ConstCLass init"); }}public class TestMain{ public static void main(String[] args) { System.out.println(ConstClass.A); }}执行结果:MIGU用final修饰某个类变量时,它的值在编译时就已经确定好放**量池了,所以在访问该类变量时,等于直接从常量池中获取,并没有初始化该类。初始化的步骤1、如果该类还没有加载和连接,则程序先加载该类并连接。2、如果该类的直接父类没有加载,则先初始化其直接父类。3、如果类中有初始化语句,则系统依次执行这些初始化语句。
-
12345678910111213141516171819202122232425262728293031323334353637class Exc{ int a; int b;} public class Except { @SuppressWarnings("finally") static int compute (){ Exc e = new Exc(); e.a = 10; e.b = 10; int res = 0 ; try{ res = e.a / e.b; System.out.println("try ……"); return res + 1; }catch(NullPointerException e1){ System.out.println("NullPointerException occured"); }catch(ArithmeticException e1){ System.out.println("ArithmeticException occured"); }catch(Exception e3){ System.out.println("Exception occured"); }finally{ System.out.println("finnaly occured"); } System.out.println(res); return res+3; } public static void main(String[] args){ int b = compute(); System.out.println("mian b= "+b); }}输出:123try ……finnaly occuredmian b= 2结论: 如果没有异常, 则执行try 中的代码块,直到 try 中的 return,接着执行 finally 中的代码块,finally 执行完后 , 回到try 中执行 return 。退出函数。12345678910111213141516171819202122232425262728293031323334353637class Exc{ int a; int b;} public class Except { @SuppressWarnings("finally") static int compute (){ Exc e = new Exc();// e.a = 10;// e.b = 10; int res = 0 ; try{ res = e.a / e.b; System.out.println("try ……"); return res + 1; }catch(NullPointerException e1){ System.out.println("NullPointerException occured"); }catch(ArithmeticException e1){ System.out.println("ArithmeticException occured"); }catch(Exception e3){ System.out.println("Exception occured"); }finally{ System.out.println("finnaly occured"); } System.out.println(res); return res+3; } public static void main(String[] args){ int b = compute(); System.out.println("mian b= "+b); }}输出:1234ArithmeticException occuredfinnaly occured0mian b= 3结论: 如果try 中有异常, 则在异常语句处,跳转到catch 捕获的异常代码块, 执行完 catch 后,再执行 finally ,跳出 try{}catch{}finally{} ,继续向下执行,不会去执行try中 后面的语句。
-
数组对于每一门编程语言来说都是一个重要的数据结构之一 当然不同语言对数组的实现及处理也不尽相同java语言中提供的数组是用来存储固定大小的同类型元素你可以声明一个被数组变量 如果numbers[100]来代替直接声明100个独立变量 number()number1......number99声明数组: 首先必须声明数组的变量 菜鸟在程序中使用数组 俩种方法:data type[] arrayrefvar; 或 data type arrayrefvar[];例如:double [] mylist ; 或者 double mylist [];创建数组:java语言中经常使用new操作符来创建数组语法:arrrayRefVar =new dataType[arraySize];例如:mylist =new double[20]上面语句做了俩件事:1:使用dataType[ArraySize]创建了一个数组2:把新创建的数组的引用赋值给变量arrayRefVar;数组变量的声明,和创建数组可以用一条语句完成;double[] mylist=new double[5];或者还可以使用下列方法创建数组;double [] mylist={1,2,3,4,5};数组的元素是通过索引来访问的数组索引从0开始 所以索引值从0到arrayRefvar。length-1.处理数组: 数组的元素类型和数组的大小都是确定的 所以当处理数组元素的时候我们通常使用基本循环或者使用for each 循环例子:for-eachu想你换 是jdk 1.5引进的一种新的循环被称为 for-each 循环或者加强循环 它能在不适用下表的情况下遍历数组格式:for(type element:array){System.out.println(element);}把上述代码在执行一次:数组作为函数的参数传递:数组可以作为参数传递给方法例如:下面的例子就是一个打印int 数组中元素的方法for(int i=0;i<array.length;i++) System.out.println(array[i]+" ");调用方法打印出数字: printArray(new int[]{1,2,3,4,5,6,78,9,});数组可以作为函数值返回int []result =new int[list.length];for(int i=0,j=result.length-1<list.length;i++,j--){ result[j]=list[i];多维数组:多维数组可以堪称是数组的数组 比如二位数组就是一个特殊的一维数组 其每一个元素都是一个一维数组如: string str[] [] =new string[3][4]多位数组的动态初始化 以二维数组为例 type[] [] typename=new type[typeLength1][Lentgh2];type可以为基本数据类型和符合数据类型 typelength1 和typelength2必须为正整数 type length1 为行数 2为列数如:int a[] [] =new int [2][3] 所以二位数组a可以看为是俩行三列的二位数组从最高维开始分别为每一维分配空间 如:String s[][] = new String[2][];s[0] = new String[2];s[1] = new String[3];s[0][0] = new String("Good");s[0][1] = new String("Luck");s[1][0] = new String("to");s[1][1] = new String("you");s[1][2] = new String("!");解析:s[0]=new String[2]和s[1]=new string[3]是最高维分配引用空间 也就是最高维限制其能 保存数据的最长的长度 然后在为其每个数组元素单独分配空间 s0=new string("good")如何引用?对二位数组中的每个元素引用方式为 arrayname[index1][index2]采用下标法:如num[1][0];arraysjava util arrays类能发辫的操作数组 它能提供的所有方法都是静态的具有以下功能 给数组赋值 fill方法对数组排序 sort方法比较数组 equals方法查找数组元素:通过binary search方法能对排序号的数组进行二分查找法操作;到此我们数组的介绍就告一段落了 喜欢的小伙伴 点赞+收藏
-
swtich case 语句判断一个变量与一系列值中某个值是否相等 每个值称为一个分支语法:switch(expression){case value: 语句break;case value:语句break;default: 语句}switch case 语句有如下几个规则:switch 语句中的变量类型可以是byte short char int 从javase7开始 switch 支持字符串string类型 同时case标签必须为字符串常量swtich语句可以拥有多个case 语句 每个case后面必须要跟一个要比较的值和冒号case语句中的值的数据类型必须与变量的数据类型相同 而且只能是常量当变量的值与case语句的值相等时 那么case语句之后的语句开始执行 直到遇到break语句出现才会跳出switch语句当遇到break语句时 switch 语句种植 程序跳转到switch语句后面的语句执行 case语句不必要包含break语句 如果没有break语句出现 程序会继续执行下一条case语句 直到出现break语句为止switch语句可以包含一个default分支 该分支一般是switch语句的最后一个分支 在default没有case语句的值和变量的值相等情况下才会执行 defalut分支则不要break语句;switch case 执行时 一定先进行匹配 匹配成功返回当前case 的值 再根据是否有break 判断是否继续输出 或者跳出判断如果case语句中没有break语句时 jvm 并不会顺序输出每一个case对应的返回值而是继续匹配 匹配不成功则返回默认 case如果case语句块中没有break语句时 匹配成功后 从当前case开始后续所有case的值都会输出如果当前匹配成功的case语句块没有break语句 则从当前case开始后续所有case 的值都会输出 如果后续的case语句块有break语句则会跳出判断
-
character类用于对单个字符进行操作character类在对象中包装一个基本类型char的值char ch ='a'char unichar='\u039a'CHAR[]charArray={‘a’,‘b’,‘c’}然而 在实际开发过程中我们经常会遇到需要使用对象而不是内置数据类型的情况 为了解决这个entire java语言在内置数据类型char提供了包装character类character ch=new character('a')在一些情况下 java编译器会自动创建一个character对象例如:将一个char类参数传递给需要一个character类型参数的方法时 那么编译器会自动的将char类型参数转换为character的的对象这种特征叫做封箱 反过来叫拆箱原始值a 封装到character提供的对象中的方法ch中 即:character ch ='a'char c =test('x');转义序列前有反斜杠(\)的字符代表转义字符 它对编译器来说是有特殊含义的下面列表展示了java 的转义序列。常用的有:\t 在文中国插入一个tab \b在文中插入一个后退键 \n在文中该处换行\r在文中该处插入回车\f在文中该处插入换页符\'在文中该处插入单引导\''在文章该处插入双引导\\在文章该处插入反斜杠例如:System.out.println("访问\"huaweilll!\"")character 方法is letter() 是否是一个字母is digit 是否是一个数字字符iswhilespace 是一个空白字符is uppercase 是否是大写字母is lowercase 指定字母的小写形式tostring()返回字符的字符串形式字符串的长度仅为1、结束!
-
java util scanner 是java5的新的特征我们可以通过scanner类来获取用户的输入。如何创建scanner对象的基本语法呢?scanner s =new scanner(system.in);接下来我们演示一个最简单的数据输入,并通过scanner类的net()和nextline()方法获取输入的字符串 在读取我们一般需要使用 hasnext和hasnextline判断是否还有输入的数据如何使用next方法呢?接下来我们接受nextline方法:上述代码中明显阐述了二者的区别next1:一定要读到有效字符后才可以结束输入2:对输入有效字符制器遇到的空白 next()方法会自动将其去掉3:只有输入有效字符后才将其后面输入的空白作为分割符或者结束符next()不能得到带有空格的字符串 nextline()可以获得空白以enter为结束符也就是说nextline()方法返回的是输入回车之前的所有字符如果要输入int或float类的数据 在scanner类中也有支持 但是在输入之前最好先使用has nextXXX()方法进行验证在使用nextXXX()方法来读取练习个例子 今天就到此结束吧 咯咯咯
-
一个if语句包含一个布尔表达式和一条或多条的语句语法:if语句的语法如下if(布尔表达式的值){//如果布尔表达式为true 将执行的语句}结果:如果布尔表达式的值为true 则执行if语句中的代码否则跳过if执行if后面语句的代码例如:if...else 语句 if语句后面可以跟else语句 当if语句的布尔表达式值为false时 else语句块会被执行语法: if(布尔表达式值){//是布尔表达式的值为true}else{是布尔表达式的值为false}if..else if..else语句if语句后面可以跟else if else 这种语句可以检查到多种可能的情况 使用if else if else语句的时候 需要注意以下几点if语句之多有一个else语句 else语句在所有的elseif之后 if语句可以有若干个else if 语句 比在在else之前一旦其中一个else if语句检查为true 其他的elseif以及else语句都将跳过执行就近原则 判断if和else相邻语法: if(布尔表达式 1){ //如果布尔表达式 1的值为true执行代码 }else if(布尔表达式 2){ //如果布尔表达式 2的值为true执行代码 }else if(布尔表达式 3){ //如果布尔表达式 3的值为true执行代码 }else { //如果以上布尔表达式都不为true执行代码 }嵌套的if else 语句 使用嵌套的if else语句是合法的 也就是说可以在另一个if或者else if语句中使用if或者else if语句语法: if(布尔表达式1){ 布尔表达式1为true执行的代码 if(布尔表达式2){布尔表达式2为true执行的代码}}你可以像if语句一样嵌套else if..else实例:到这里 ifelse 的语句就结束了 感谢大家阅读 喜欢的话记得点赞+收藏哦!
-
java循环结构和其他语言类似 包括 for while 和do while顺序结构的程序语句只能被执行一次 如果想执行同样的操作多次就需要使用循环结构for循环是从java5中引入的主要用于数组while循环 while循环是基本的循环 结构为while(布尔表达式的值){ //循环内容}只要boolean表达式为真就会一直循环下去 例如: int x = 10; while( x < 20 ) { System.out.print("value of x : " + x ); x++; System.out.print("\n");}结果会以10为初始值 每次步进为1的递增 直到 while中的布尔值为false时跳出循环do while 循环对于while语言而将 如果不满足条件则不能进入循环 但有时候我们需要即使不满足条件也要至少执行一次 do while 循环和while 循环类似 不同的是 do while 循环至少循环一次基本结构: do { //代码}while(布尔表达式的值);注意:布尔表达式在循环体的后面 所以语句在检测布尔表达式之前就已经执行了如果布尔表达式的值为true 则语句快将一直执行下去 直到布尔表达式的值为假例如int x = 100;do{ System.out.print("value of x : " + x ); x++; System.out.print("\n");}while( x < 220 );结果会以100为初值 +1为步进当不满足布尔表达式时跳出循环 且至少循环一次for循环 虽然所有循环结构都可以用while dowhile 表示 但java也提供了另一种循环语句 for循环 使一些循环结构变得更加简单 for循环执行的次数是在执行前就确定的 格式:for(初始值;布尔表达式;步进){//代码语句}关于for循环有以下几点说明:最先执行初始化步骤 可以声明一种类型 但可初始化一个或多个循环控制变量 也可以是空语句检查布尔表达式的值 如果为true 循环体被执行如果为false 则终止循环 开始执行循环体后面的语句执行一次循环后 更新循环控制变量再次检查布尔表达式的值 循环执行上面的过程例如:java 增加for循环 for循环是从java5开始引入的一种主要用于数组的增强型for循环for(声明语句:表达式)声明语句:声明新的局部变量 该变量的类型必须和数组元素的类型匹配 其作用域定在循环语句块 其值与此时数组元素的值是等价的表达式:表达式是要访问的数组名 或者返回值为数组的方法 例如:break关键字 break主要用在循环语句或者switch语句中用来跳出整个语句块break跳出最里层的循环 并且继续执行该循环下面的语句;语法:break是循环结构中的一条语句 break;实例代码:continue关键字 continue适用于任何循环控制结构中作用是让程序立刻跳转到下一次循环的迭代中去在for循环中continue语句使程序立即跳转到更新的语句中在while或者dowhile语句中 程序立即跳转到布尔表达式的判断语句中。语法:continue就是循环体中一条简单的语句:cotinue;这里 java的循环结构就介绍完了 喜欢的小伙伴记得点赞+收藏哈!
-
访问控制和继承 请注意以下方法继承的规则:1:父类中声明为public的放啊在子类中必须也要用public方法2:父类中声明为protected的方法在子类中要么声明protected要么声明为public不能声明为private3:父类中声明为private的方法不能够被继承非访问修饰符为了实现一些其他的功能 java提供了俩个修饰符 为访问和非访问修饰符static修饰符 用离开修饰类方法和类类变量final修饰符用来修饰 类 方法和变量 final修饰的类不能够被继承 修饰的方法不能被继承类重新定义 修的变量为常量是不可更改的abstract 修饰符用来创建抽象类和筹建方法synchronized主要用来线程的编辑static修饰符静态变量:static关键字用来声明独立于对象的静态变量 无论一个类实例化多少对象他的静态变量只有一份拷贝 静态变量也成为类变量 局部变量不能被声明为static变量静态方法:static关键字用声明独立于对象的静态方法 静态方法 不能使用类的非静态变量 静态方法从参数列表得到数据 然后计算这些数据对类变量和方法的访问可以直接使用lclassname.variablename和classname.methodname的方式访问。 class InstanceCounter { private int numInstances = 0; protected int getCount() { return numInstances; } private void addInstance() { numInstances++; } InstanceCounter() { InstanceCounter.addInstance(); } public static void main(String[] arguments) { System.out.println("Starting with " + InstanceCounter.getCount() + " instances"); for (int i = 0; i < 500; ++i) { new InstanceCounter(); } System.out.println("Created " + InstanceCounter.getCount() + " instances"); } }}结果为:Starting with 0 instancesCreated 500 instancesfinal 修饰符 final变量 final表示最后的含义 变量一旦被符之后不能被重新赋值 被final修饰的实力变量必须显示指定的初始值 (重点)final方法 父类中的final方法可以被子类继承 但是不能被子类从写声明final方法的主要目的是防止该方法的内容被修改如下: public final void changename(){//方法体}final类 性质:final类不能被继承 没有任何类可以继承final类的任何特性例:public final classtext{//类体}abstract修饰符抽象类性质:抽象类不能实例化对象 声明抽象类的唯一目的是为了将来对该类进行扩充一个类不能同时被抽象类和final类修饰 如果一个类包含抽象方法 那么该类一定要声明为抽象类 否则编译出错例:abstract class cat{private double price;private String model;private String year;public abstract void gofast();public abstract void change color();抽象方法抽象方法是一种没有任何实现的方法 该方法的具体实现由子类提供抽象方法不能被声明为final和静态任何继承抽象类的子类必须实现父类的all抽象方法,除非该子类也是抽象类如果一个类包含若干个抽象方法那么该类必须声明为抽象类 抽象类可以不包含抽象方法。即 抽象类可以不包含抽象方法 但是抽象方法必须在抽象类中声明抽象方法的声明以分号结尾:如 public abstract sample();public abstract class SuperClass{ abstract void m(); //抽象方法}class SubClass extends SuperClass{ //实现抽象方法 void m(){ ......... }}抽象类到此就彻底结束了~感谢大家的耐心阅读 记得点赞+收藏哈
-
java语言中提供了很多修饰符 主要分为俩类:1:访问修饰符2:非访问修饰符修饰符用来定义类 方法或者变量 通常放在语句的最前端 举个例子:访问类修饰符 java中可以使用访问控制符来保护对类 变量 方法 和构造方法的访问 java支持4中不同访问的权限default 默认 什么也不写 不适用任何修饰符private 在用一个类内可见 使用对象 变量 方法 注意 不能修饰类 外部类public 对所有类可见 使用对象:类 接口 变量 方法protected 对同一包内的类和所有子类可见 使用对象:变量 方法 注意和private意义不能修饰外部类相信大家学习java时老师会给大家列一个表格 来一次人间清醒 这里就不画了哈默认访问修饰符 不适用任何关键字使用默认访问修饰符生命的变量和方法对同一个包内得到类是可见的 接口的变量都生命为:public static final 而接口里的方法默认情况下访问权限为 public像这样变量和方法的声明可以不适用任何修饰符String version = "1.5.1"; boolean processOrder() { return true; }私有访问修饰符 private 私有访问修饰符是最严格 的访问级别 所以被称为private的方法 变量和构造方法只能被所属类访问 并且类和接口不能声明为private 生命为私有访问类型的变量只能通过类中公共的getter方法被外部所访问private 访问修饰符的使用主要用来因此类的实现细节和保护类的数据如:public class cat{ private String format; public String getFormat; return this.format;}public void setFormat(String format){ this.format=format;}实例中logger类中的format变量为私有变量 所以其他类不能直接得到和设置该变量的值 为了使其他类能够操作该变量 定义了俩个public方法 getformat()和setformat(string)get是返回format的值 set是设置format的值共有访问修饰符-public被声明为public的类 方法构造方法和接口能够被任何其他访问类所访问如果有几个相互访问的public类分布在不同的包中 则需要导入相应的public类所在的包 由于类的继承性 类所有的公有方法和变量都能被其子类继承例:public static void main(String[]arguments){//.......}java程序的main方法必须设置成共有的 否则 java解释器将不执行该类受保护的访问修饰符 protected从俩点入手:子类与基类在一个包中 子类和基类不在同一个 包中1:被声明为protected的变量 方法和构造器能被同一个包中的任何其他类所访问2:那么在子类中 子类实例可以访问其从基类继承而来的protected方法而不能访问基类实例的protected方法protected可以修饰数据成员 构造放啊 方法成员 唯独不能修饰外部类接口及接口成员和成员方法不能生命为protected 子类能访问protected修饰符生命的方法和变量 这样就能保护不想挂按的类使用这些方法和变量下面的弗雷使用了protected访问修饰符子类重写弗雷的openspeaker()方法public class Main { public boolean main(String[] args) { class AudioPlayer { protected boolean openSpeaker(Speaker sp) { // 实现细节 } } class StreamingAudioPlayer extends AudioPlayer { protected boolean openSpeaker(Speaker sp) { // 实现细节 } }}如果把openspeaker()方法声明为private 那么除了audioplayer之外的类将不能访问该方法。如果把openspeaker()声明为public 那么所有的类都能够访问该方法如果我们只想让该方法对其所在类的子类可见 则将该方法声明为protected第四次java之修饰符结束一半了哦 写文章不易 建议点赞+收藏!
-
用途:正则表达式用来定义字符串模式、还可以用来搜索 编辑 处理文本。什么是正则表达式呢?其实一个字符串就是一个简单的正则表达式 如:hello java 匹配到的正则表达式就”hello java“ 那就有人问了 特殊符号例如 点号 . 也是正则表达式么?回答是对的点号匹配到的字符 如 "a" 或"1"举例: this is java 匹配字符串 "this is java" this\s+is\s+java 注意字符串中的\s+ 匹配单词this后面的\s可以匹配多个空格 之后匹配is字符串 在之后\s匹配多个空格然后在跟上java字符串 匹配结果:this is java ^\d+(\.\d+)? ^定义了以什么开始 \d匹配一个或多个数字 ?设置括号内的选项是可选的 \匹配"" 可以匹配的例子: "5","1.5","3.8"学过perl的都知道其实java 的正则表达式和perl是最为相似的java util regex包主要包括以下三个类:pattern类: 作用:pattern对象是一个正则表达式的编译。pattern类没有公共的构造方法 要创建一个pattern对象 必须先调用其公共静态编译方法 它返回一个pattern对象 该方法接受一个正则表达式的参数matcher类: 作用:matcher对象是对输入字符串进行解释和匹配操作的引擎 与pattern类一样 matcher也没有公共的构造方法 需要调用pattern对象的matcher方法获得一个matcher对象patternsyntaxexception 作用:其是一个非强制的异常类 它表示一个正则表达式模式中的语法错误举个例子吧:学过正则表达式的知道名词:捕捉组捕捉组是把多个字符当一个单独的单元进行处理的方法它提供对括号内的字符分组来创建例如:正则表达式(cat)创建了一个单一分组 组内包含:"d" "o" "g"捕捉组通过从左至右计算其开括号来编号例如 ((A)(B)(C))有这样四个分组:(A) ((A)(B)(C)) )(B)(C) (C) 可以通过调用matcher对象的groupcount 方法来查看表达式到底分了多少组 groupcount 方法返回一个int值 表示matcher对象当前有多个捕捉组还有一个特殊的组group(0) 它总是代表整个表达式 该组不包括在groupcount的返回值中举个实例:正则表达式语法:在其他语言中,\\表示我想要在这边负责vu挨打事故这个i部分插入一个普通的反斜杠 请不要给他任何特殊的意义;(其实就是c语言中的转义字符)在java中\\表示我要插入一个正则表达式的反斜杠,所以后面的字符有特殊的意义所以在其他的语言中 如 c pert 一个反斜杠就足以具有转义的作用 而在java中正则表达式中则需要有俩个反斜杠菜鸟被解析为其他语言中的转义作用。这就是为什么表示一位数字的正则表达式是\\d而 表示一个普通的反斜杠是\\\\索引方法:pubilc int start() 返回以前匹配的初始索引public int start(int group) 返回在以前的匹配操作期间 由给定组所捕获的子序列的初始索引public int end()返回最后匹配字符之后的偏移量public int end(int group) 返回在以前的匹配操作期间由给定组所捕获子序列的最后字符之后的偏移量举例:那第三期的java之正则表达式就到此结束啦~喜欢的小伙伴点赞加收藏~
-
java util包中提供了date类来封装当前的日期和时间。date类提供俩个构造函数来实例化date对象.1:第一个构造函数使用当前日期和时间来初始化对象。 date()2:第二个构造函数接受一个参数,该参数必须从1970年1月1日1分1秒起。date(long millisec)date创建以后可以调用下面的方法 6种方法1:boolean afther (date date) 含义:若当调用此方法的Date对象在指定日期之后返回true,否则返回false。2: boolean before(date date)含义:若当调用此方法的Date对象在指定日期之前返回true,否则返回false。3: object clone() 含义:返回该对象的副本4: int compare to(date date) 含义:当调用次此方法的date对象和指定日期作比较时 二者相等时返回0 调用对象在指定日期之前返回负值,调用对象在指定日期之后返回正值。5:long get time() 含义:返回自1970年开始对象得到毫秒数6: int has code() 含义:返回调用对象的哈希值举个例子:如何获取当前日期和时间呢?使用datre对象的tostring()方法打印当前日期和时间上次我们说到 字符串有比较 那日期的比较呢?又有几种形式呢?1:使用gettime() 方法获取俩个日期 然后比较二者值大小2:使用上面提到过的before() afther()和equals()例如 3月比4月早 newdate(21,3)before(new date(21,4))结果返回true3:使用compareto() 的方法 该方法由comparable接口定义,date实现了这个接口并提供类调用时的方法。格式化:如何利用类放啊使得日期被格式化呢?利用simpledateformat是一个以语言环境敏感的方式来格式化和分析日期的类。好处:该类允许你选择任何用户自定义的日期时间格式来执行simpledateformat ft=new simpledateformat(“yyyy-mm-dd hh:mm:ss”);这一行确定了转化的格式:其中yyyy是完整的公元年,mm是month的首字符 月份 以此类推常见的日期格式有:y 四位年份 yyyy 2021m 月份 可以是英文或阿拉伯数字 如:july或07d 一个月的日期 10 20 30h 采用十二进制显示日小时 1~12m 分钟数 1~60d 一年中的日子 360天f 一个月中第几周的星期几 2 (second wed injuly)s 秒数 1~60秒如何用printf格式化日期呢?c 包括全部日期和时间信息f 年月日格式d 月日年格式r hh:mm:ss pm格式综合上述方法实现下:
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签