当前位置: 首页 > 科技观察

Java程序员必须掌握的4个基础知识!

时间:2023-03-19 01:34:24 科技观察

大家大概从学生时代就开始使用Java了,我们一直在学习Java,但是Java中总有一些概念是模棱两可的,不管是初学者还是高级程序员。因此,本文的目的是澄清这些概念。看完这篇文章,你会对这些概念有更深的理解,也能搞清楚所有灰色的东西。在本书中,我们将讨论匿名内联类、多线程、同步和序列化。1.匿名类Java中的匿名类很像分部类或内联类,只是没有名字。我们可以在定义和实例化类时利用匿名类。仅当本地类仅使用一次时才应这样做。匿名类不能有显式定义的构造函数。相反,每个匿名类都隐式定义了一个匿名构造函数。创建匿名类有两种方法:扩展现有类(抽象或具体)创建接口理解代码的最好方法是先阅读它,所以让我们先看代码。interfaceFootball{voidkick();}classAnonymousClass{publicstaticFootballfootball=newFootball(){@Overridepublicvoidkick(){System.out.println("NestedAnonymousClass.");}};publicstaticvoidmain(String[]args){//anonymousclassinsidethemethodFootballfootballObject(newFootballObject=){@Overridepublicvoidkick(){System.out.println("AnonymousClass");}};footballObject.kick();AnonymousClass.football.kick();}}可以在类和函数代码块中创建匿名类。您可能知道,可以使用接口或通过扩展抽象类或具体类来创建匿名类。在上面的例子中,我首先创建了一个接口Football,然后在该类的范围内实现了匿名类和main()方法。Football也可以是抽象类,也可以是接口旁边的顶级类。Football可以是一个抽象类,看下面的代码。publicabstractclassFootball{abstractvoidkick();}匿名类不仅可以是抽象类,也可以是具体类。//normalorconcreteclasspublicclassFootball{publicvoidkick(){}}//endofclassscope。如果Football类没有无参构造函数怎么办?我们可以访问匿名类中的类变量吗?我们需要重载匿名类中的所有方法吗?//normalorconcreteclasspublicclassFootball{protectedintscore;publicFootball(intscore){this.score=score;}publicvoidscore(){System.out.println("Score"+score);};publicvoidkick(){}publicstaticvoidmain(String[]args){Footballfootball=newFootball(7){@Overridepublicvoidscore(){System.out.println("Anonymousclassinsidethemethod"+score);}};football.score();}}//endofclassscope。创建匿名类时可以使用任何构造函数。请注意,这里也使用了构造函数的参数。匿名类可以扩展顶级类并实现抽象类或接口。因此,访问控制规则仍然适用。我们可以访问protected变量,但是如果我们把它们改成private就不能访问了。由于在上面的代码中扩展了Football类,我们不需要重载所有的方法。但是,如果它是接口或抽象类,则必须为所有未实现的方法提供实现。不能在匿名类中定义静态初始化方法或成员接口。匿名类可以有静态成员变量,但必须是常量。匿名类的目的:1.更清晰的项目结构:通常我们需要随时更改某个类的某些方法的实现时,就会使用匿名类。这样做就无需向项目添加新的*.java文件来定义顶级类。这非常有效,尤其是当顶级类只使用一次时。2.UI事件监听器:在图形界面应用程序中,匿名类最常见的用途是创建各种事件处理程序。例如下面的代码:button.setOnClickListener(newView.OnClickListener(){publicvoidonClick(Viewv){//yourhandlercodehere}});我们创建了一个实现setOnClickListener接口的匿名类。它的onClick方法在用户单击按钮时被触发。2、多线程Java中的多线程可以同时执行多个线程。线程是轻量级的子进程,是最小的处理单元。使用多线程的主要目的是最大化CPU使用率。我们使用多线程而不是多处理,因为线程更轻,可以在同一个进程内共享内存空间。多线程用于实现多任务处理。线程的生命周期如上图所示。线程的生命周期主要有五种状态。让我们依次解释每个状态。new:线程创建实例后进入new状态,也就是第一个状态,但线程还没有准备好运行。Runanble:调用线程类的start()方法,状态会由new变为Runnable,表示线程可以运行,但是什么时候真正开始运行取决于Java线程调度器,因为调度器可能很忙执行其他线程。线程调度器会以FIFO(先进先出)的方式从线程池中选择一个线程。3.阻塞:有很多情况会导致线程阻塞,比如等待I/O操作,等待网络连接等等。此外,具有更高优先级的线程可以将当前运行的线程变为阻塞状态。4.等待:一个线程可以调用wait()进入等待状态。当其他线程调用notify()时,会回到runnable状态。5.终止:当start()方法退出时,线程进入终止状态。为什么要使用多线程?使用线程可以让Java应用程序同时做多件事情,从而加快运行速度。用技术术语来说,线程可以帮助您在Java程序中实现并行操作。由于现代CPU非常快并且可能包含多个内核,因此无法仅通过一个线程使用所有内核。要记住的要点多线程可以更好地利用CPU。提高响应速度和用户体验减少响应时间同时为多个客户端提供服务创建线程主要有两种方式:扩展Thread类实现Runnable接口通过扩展Thread类创建线程创建一个扩展Thread类的类。此类应覆盖Thread类中的run()方法。线程在run()方法中开始其生命周期。我们创建新类的一个对象,然后调用start()方法开始执行线程。在Thread对象中,start()将调用run()。publicclassMultithreadingTestextendsThread{publicvoidrun(){try{System.out.println("Thread"+Thread.currentThread().getName()+"isnowrunning");}catch(Exceptionex){ex.printStackTrace();}}publicstaticvoidmain(String[]args){for(inti=0;i<10;i++){MultithreadingTestmultithreadingTest=newMultithreadingTest();multithreadingTest.start();}}}也可以通过接口创建类。以下代码创建了一个实现java.lang.Runnable接口并覆盖run()方法的类。然后我们实例化一个Thread对象并调用对象的start()方法。publicclassMultithreadingTestimplementsRunnable{@Overridepublicvoidrun(){System.out.println("Thread"+Thread.currentThread().getName()+"isnowrunning");//改变bodyofgeneratedmethods,chooseTools|Templates.}publicstaticvoidmain(String[]args){for(inti=0;i<10;i++){Threadthread=newThread(newMultithreadingTest());thread.start();}}}关注微信公众号:Java技术栈,后台回复:多线程,可以getme整理的最新N篇多线程教程,全是干货。Thread类和Runnable接口如果扩展Thread类,则不能扩展更多的类,因为Java不允许多重继承。通过接口可以实现多重继承。所以最好使用接口而不是Thread类。如果扩展了Thread类,它还包含了一些方法,比如yield()、interrupt()等,这些方法在我们的程序中可能用不到。在Runnable接口中,没有这些无用的方法。3.同步同步是指多线程同步。同步代码块一次只能由一个线程执行。同步在Java中是一个很重要的概念,因为Java是多线程语言,多个线程可以并行执行。在多线程环境下,Java对象的同步,或者说Java类的同步,是非常重要的。为什么要同步?如果代码在多线程环境中执行,则需要同步多个线程之间共享的对象,以避免破坏状态或导致任何不可预测的行为。在深入同步的概念之前,让我们先了解一下这个问题。classTable{voidprintTable(intn){//methodnotsynchronizedfor(inti=1;i<=5;i++){System.out.print(n*i+"");try{Thread.sleep(400);}catch(Exceptione){System.out.println(e);}}}}classMyThread1extendsThread{Tablet;MyThread1(Tablet){tthis.t=t;}publicvoidrun(){t.printTable(5);}}classMyThread2extendsThread{Tablet;MyThread2(Tablet){tthis.t=t;}publicvoidrun(){t.printTable(100);}}classTestSynchronization1{publicstaticvoidmain(Stringargs[]){Tableobj=newTable();//onlyoneobjectMyThread1t1=newMyThread1(obj);MyThread2t2=newMyThread2(obj);t1.start();t2.start();}}运行这段代码并注意到输出非常不稳定,因为没有同步。让我们看看程序的输出。输出:100520010300152040050025classTable{synchronizedvoidprintTable(intn){//synchronizedmethodfor(inti=1;i<=5;i++){System.out.print(n*i+"");try{Thread.sleep(400);}catch(Exceptione){System.out.println(e);}}}}classTestSynchronization3{publicstaticvoidmain(Stringargs[]){finalTableobj=newTable();//onlyoneobjectThreadt1=newThread(){publicvoidrun(){obj.printTable(5);}};Threadt2=newThread(){publicvoidrun(){obj.printTable(100);}};t1.start();t2.start();}}在printTable()方法中加上synchronized,然后synchronized方法在执行完成之前不允许其他线程进入。下面的输出非常稳定。输出:510152025100200300400500同样,Java类和对象也可以同步。注意:我们不一定需要同步整个方法。有时最好只同步方法的一小部分。Java的同步代码段可以实现这一点。4.序列化Java中的序列化是一种将对象的状态写入字节流的机制。相反的操作称为反序列化,将字节流转换为对象。序列化和反序列化过程与平台无关,即在一个平台上序列化的对象可以在另一个平台上反序列化。序列化时调用ObjectOutputStream类的writeObject()方法,反序列化时调用ObjectInputStream类的readObject()方法。下图中,Java对象被转换为字节流,然后存储在各种形式的存储中。这个过程称为序列化。图中右侧,内存中的字节流被转换为Java对象。这个过程称为反序列化。为什么序列化很明显,创建的Java类在程序执行结束或终止后被销毁。为了避免这个问题,Java提供了序列化功能,通过序列化功能可以存储对象或者持久化状态,以备后用或者在其他平台上使用。下面的代码演示了这个过程。publicclassEmployeeimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateStringserializeValueName;privatetransientintnonSerializeValueSalary;publicStringgetSerializeValueName(){returnserializeValueName;}publicvoidsetSerializeValueName(StringserializeValueName){this.serializeValueName=serializeValueName;}publicintgetNonSerializeValueSalary(){returnnonSerializeValueSalary;}publicvoidsetNonSerializeValueSalary(intnonSerializeValueSalary){this.nonSerializeValueSalary=nonSerializeValueSalary;}@OverridepublicStringtoString(){return"Employee[serializeValueName="+serializeValueName+"]";}}importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.ObjectOutputStream;publicclassSerializingObject{publicstaticvoidmain(String[]args){EmployeeemployeeOutput=null;FileOutputStreamfos=null;ObjectOutputStreamoos=null;employeeOutput=newEmployee();employeeOutput.setSerializeValueName("Aman");employeeOutput.setNonSerializeValueSalary(50000);try{fos=newFileOutputStream("Employee.ser");oos=newObjectOutputStream(fos);oos.writeObject(employeeOutput);System.out.println("SerializeddataissavedinEmployee.serfile");oos.close();fos.close();}catch(IOExceptione){e.printStackTrace();}}}输出:SerializeddataissavedinEmployee.serfile.importjava.io.FileInputStream;importjava.io.IOException;importjava.io.ObjectInputStream;publicclassDeSerializingObject{publicstaticvoidmain(String[]args){EmployeeemployeeInput=null;FileInputStreamfis=null;ObjectInputStreamois=null;try{fis=newFileInputStream("Employee.ser");ois=newObjectInputStream(fis);employeeInput=(Employee)ois.readObject();系统.out.println("SerializeddataisrestoredfromEmployee.serfile");ois.close();fis.close();}catch(IOException|ClassNotFoundExceptione){e.printStackTrace();}System.out.println("Nameofemployeeis:"+employeeInput.getSerializeValueName());System.out.println("Salaryofemployeeis:"+employeeInput.getNonSerializeValueSalary());}}Output:SerializeddataisstoredfromEmployee.serfileNameofemployeeis:AmanSalaryofemployeeis:0需要记住的要点如果父类实现了Serializable接口,那么子类就不需要实现,反之则不一定只能保存非静态数据成员在序列化期间。序列化期间不保留静态和临时数据成员。所以,如果你不想保存一个非静态数据成员,你可以将它设置为transient。在反序列化期间不调用对象的构造函数。关联对象必须实现Serializable接口。5.总结1.首先,我们解释了匿名类,它的目的和用法。2.其次,我们讨论了Java中的多线程,线程的生命周期,以及它们的用途。3.同步只允许一个线程进入同步方法或代码块访问资源,其他线程必须在队列中等待。4.序列化是存储对象状态供以后使用的过程。