
1、Struts2和SpringMVC的區別
(1)設計理念:前者為有狀態的Action(均為多例),Action對象屬性字段承載請求、響應,后者一般為無狀態的Controller,請求直接封裝到方法的參數中;
(2)集中訪問點不同:都屬于前端控制器,用于接收請求、處理請求和生成響應,但集中訪問點不同,前者為Filter,后者為Servlet;
(3)請求處理粒度不同:前者一個Action對應一個請求上下文,后者一個方法對應一個請求上下文,因此更容易實現Rest;
(4)攔截器機制不同:Struts2和SpringMVC的攔截器機制均是對AOP理念的應用,但Struts2的interceptor機制是通過代理機制(ActionProxy)+責任鏈模式實現的,而SpringMVC的interceptor機制實現比較簡單,其通過循環的方式在handler處理請求前后分別調用preHandle()方法和postHandle()方法對請求和響應進行處理,與Spring AOP、責任鏈模式等基本無關;
(5)對ajax的支持不同:前者需要插件或者手動轉化,而后者集成了對Ajax請求的處理(HttpMessageConverter);
(6)與Spring的整合:前者需要插件,后者無縫整合(子容器);
(7)配置/效率:后者幾乎是零配置,開發效率更高。
2、Spring中IOC的理解
(1)超級大工廠:對象控制權由調用者移交給容器,使得調用者不必關心對象的創建和管理,專注于業務邏輯開發;
(2)優秀的解耦方式,解耦對象間的依賴關系,避免通過硬編碼的方式耦合在一起;
(3)底層實現:反射機制;
3、Spring中AOP的理解
(1)一種新的模塊化方式,專門處理系統各模塊中的交叉關注點問題,將具有橫切性質的系統級業務提取到切面中,與核心業務邏輯分離(解耦);
(2)便于系統的擴展,符合開-閉原則;
(3)動態AOP的實現,JAVA動態代理(接口代理)與cglib(類代理),具體由Bean后處理器生成代理;
(4)AOP理念實踐:Spring AOP,Java Web Filter,Struts2 Interceptor, SpringMVC Interceptor,…
4、JVM 基礎
4.1內存模型
(1)程序計數器:線程私有,CPU調度的基本單位,用于保證線程切換(程序能夠在多線程環境下連續執行);
(2)棧(服務Java方法虛擬機棧、服務Native方法的本地方法棧):線程私有,局部變量/引用,棧深度(SOF)/無法申請內存(OOM);
(3)堆(Java代碼可及的Java堆和JVM自身使用的方法區):線程共享,對象分配和回收主要區域,OOM;
4.2垃圾回收機制
(1)Stop-the-World
JVM由于要執行GC而停止了應用程序的執行稱之為Stop-the-World,該情形會在任何一種GC算法中發生。當Stop-the-world發生時,除了GC所需的線程以外,所有線程都處于等待狀態直到GC任務完成。事實上,GC優化很多時候就是指減少Stop-the-world發生的時間,從而使系統具有高吞吐 、低停頓的特點。
(2)JAVA堆的回收
關于Java對象的回收主要考慮以下兩個問題:哪些對象可以被回收、怎么回收(有哪些回收算法以及有哪些垃圾回收器)。
①判斷對象是否可被回收:引用計數法(相互引用)、可達性算法(對象的引用鏈是否可達,GCRoots)
②垃圾回收算法:標記-清除算法(內存碎片)、復制算法(垃圾回收較為頻繁、對象存活率較低的新生代)、標記-整理算法(垃圾回收不頻繁、對象存活率較高的老年代)、分代收集算法。
③垃圾回收器:串行收集器(新生代、老年代)、并行收集器(新生代、老年代)、并行清除收集器(并發,新生代,追求高吞吐)、CMS收集器(并發,老年代,標記-清除算法,追求低停頓)、G1垃圾收集器(整個堆,追求低停頓)
附:
GC Roots一般包括虛擬機棧中引用的對象,本地方法棧中引用的對象,方法區中類靜態屬性引用的對象、方法區中常量引用的對象。
附:
GC Roots一般包括虛擬機棧中引用的對象,本地方法棧中引用的對象,方法區中類靜態屬性引用的對象、方法區中常量引用的對象。
分代收集算法的基本思想是:不同的對象的生命周期(存活情況)是不一樣的,而不同生命周期的對象位于堆中不同的區域,因此對堆內存不同區域采用不同的策略進行回收可以提高 JVM 的執行效率。Minor GC 發生頻率較高,不一定等 Eden區滿了才觸發;Major GC在老年代滿時觸發,對年輕代和老年代進行回收。
(3)方法區回收
①對常量池的回收。
②對類型的卸載:該類的所有實例被回收,該類的ClassLoader被回收,不存在對該類的Class對象的引用。
4.3 OOM/SOF
(1)OOM for Heap:內存泄露(GC Roots的引用鏈,對象的生命周期超出預期)或者內存溢出(調節JVM參數 -Xms,-Xmx 等)
(2)OOM for Stack:一般在單線程程序中不會出現;在多線程環境下,無法申請到足夠的內存去創建線程
(3)SOF for Stack:程序是否有深度遞歸。
(4)OOM for Perm :用到像Spring等框架的時候,常常會動態地生成大量的類導致永久代不夠用而導致OutOfMemoryError: PermGen Space異常(調大 -XX:MaxPermSize)
5、JVM 調優
JVM 調優的主要目標是使系統具有高吞吐、低停頓的特點,其優化手段應從兩方面著手:Java虛擬機和Java應用程序。前者指根據應用程序的設計通過虛擬機參數控制虛擬機邏輯內存分區的大小以使虛擬機的內存與程序對內存的需求相得益彰;后者指優化程序算法,降低GC負擔,提高GC回收成功率。以下是一些常用的JVM調優工具:
(1)Jconsole 與 Visual VM
JConsole 與 Visual VM 都是JDK自帶的 Java 性能分析器,可以從命令行或在 GUI shell 中運行,從而可以輕松使用 JConsole來監控 Java 應用程序性能和跟蹤 Java 中的代碼,其可以從JAVA_HOME/bin/這個目錄下找到。使用 Jconsole 監測死鎖示例如下:
(1)Jstack
JDK自帶的命令行工具,可以查看某個Java進程內的線程堆棧信息,主要用于線程Dump分析。
(1)JPS
jps位于jdk的bin目錄下,其作用是顯示當前系統的java進程情況及其id。
6、責任鏈(CoR)模式
目的:請求的發送者與請求的處理者解耦,便于動態的重新組織鏈和分配責任。
角色:抽象處理者、具體處理者、客戶端。
UML:
傳統責任鏈(CoR)模式的缺點在于:具體處理角色存在著共同的實現責任鏈結構的行為行為,即責任鏈的建立和指派包含在實現角色的類中,并沒有抽象出來,這直接導致責任鏈的指派不夠靈活。因此,改進的CoR模式為:使用AOP理念將責任鏈結構的實現用切面抽象出來,使得各個對象只關注自身必須實現的功能性需求,準確地分離出責任鏈模式中不同角色的共同行為,例如,
改進后責任鏈(CoR)模式的應用是比較廣泛的,包括 Java Web Filter(鏈式調用),Struts2 Interceptor(Action代理)和SpringMVC等。
7、單例模式
單例模式核心在于為整個系統提供一個唯一的實例,為整個系統提供一個全局訪問點。單例模式從實現上可以分為餓漢式單例和懶漢式單例兩種,前者天生就是線程安全的,后者則需要考慮線程安全性,常見的線程安全的懶漢式單例的實現有內部類式和雙重檢查式兩種。下面給出單例模式幾種常見的形式:
(1) 餓漢式單例
(1) 懶漢式單例
(1) 線程安全的懶漢式單例 —— 內部類方式
內部類方式線程安全懶漢式單例的內在原理在于:虛擬機會保證一個類的類構造器<clinit>()在多線程環境中被正確的加鎖、同步,如果多個線程同時去初始化一個類,那么只會有一個線程去執行這個類的類構造器<clinit>(),其他線程都需要阻塞等待,直到活動線程執行<clinit>()方法完畢。特別需要注意的是,在這種情形下,其他線程雖然會被阻塞,但如果執行<clinit>()方法的那條線程退出后,其他線程在喚醒之后不會再次進入/執行<clinit>()方法,因為在同一個類加載器下,一個類型只會被初始化一次。
(4) 線程安全的懶漢式單例——雙重檢查方式
8、類的生命周期及其初始化時機
類的生命周期主要包括加載、鏈接、初始化、使用和卸載五個階段,如下圖所示:
(1) 遇到new、getstatic、putstatic或invokestatic這四條字節碼指令時:注意,newarray指令觸發的只是數組類型本身的初始化,而不會導致其相關類型的初始化,比如,new String[]只會直接觸發String[]類的初始化,也就是觸發對類[Ljava.lang.String的初始化,而直接不會觸發String類的初始化時,如果類沒有進行過初始化,則需要先對其進行初始化。生成這四條指令的最常見的Java代碼場景是:
①使用new關鍵字實例化對象的時候;②讀取或設置一個類的靜態字段(被final修飾,已在編譯器把結果放入常量池的靜態字段除外)的時候;③調用一個類的靜態方法的時候。
(2) 對類進行反射調用時:使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化。
(3) 初始化子類時:當初始
化一個類
時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
(4) 虛擬機啟動時:當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
(5) 當使用jdk1.7動態語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的解析結果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且這個方法句柄所對應的類沒有進行初始化,則需要先出觸發其初始化。
9、類加載過程中各階段的作用
9.1加載(Loading)
(1)通過一個類的全限定名來獲取定義此類的二進制字節流(并沒有指明要從一個Class文件中獲取,可以從其他渠道,譬如:網絡、動態生成、數據庫等);
(2)將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構;
(3)在內存中(對于HotSpot虛擬機而言就是方法區)生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口;
9.2鏈接(Linking)
(1)驗證:驗證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。
(2)準備(Preparation):準備階段是正式為類變量(static 成員變量)分配內存并設置類變量初始值(零值)的階段,這些變量所使用的內存都將在方法區中進行分配。
(3)解析(Resolution):解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程。
9.3初始化
初始化階段是執行類構造器<clinit>()方法的過程。虛擬機會保證一個類的類構造器<clinit>()在多線程環境中被正確的加鎖、同步,如果多個線程同時去初始化一個類,那么只會有一個線程去執行這個類的類構造器<clinit>(),其他線程都需要阻塞等待,直到活動線程執行<clinit>()方法完畢。特別需要注意的是,在這種情形下,其他線程雖然會被阻塞,但如果執行<clinit>()方法的那條線程退出后,其他線程在喚醒之后不會再次進入/執行<clinit>()方法,因為在同一個類加載器下,一個類型只會被初始化一次。
10、對象的創建過程
在Java中,創建一個對象常常需要經歷如下幾個過程:父類的類構造器<clinit>() -> 子類的類構造器<clinit>() -> 父類的實例構造器(成員變量和實例代碼塊,父類的構造函數) -> 子類的實例構造器(成員變量和實例代碼塊,子類的構造函數)。其中,類構造器<clinit>()由靜態變量和靜態語句塊組成,而類的實例構造器<init>()類的實例變量/語句塊以及其構造函數組成。
11、雙親委派模型
雙親委派模型很好地解決了類加載器的統一加載問題:越基礎的類由越上層的加載器進行加載,進而保證Java類型體系中最基礎的行為,防止應用程序變得混亂。比如,java.lang.Object 類總是由啟動類加載器進行加載,因此Object類在程序的各種類加載器環境中都是同一個類型(是否是同一類型由類加載器與類本身共同決定)。
(1)BootStrap:引導類加載器:加載都是最基礎的文(JRE/lib/rt.jar)
(2)ExtClassLoader:引導類加載器:加載都是最基礎的文件(JRE/lib/ext/*.jar)
(3)AppClassLoader:ClassPath指定的所有jar或目錄。
12、異常機制
Java體系中異常的組織分類如下圖所示,所有異常類型的根類為 Throwable,具體包括兩大類:Error 與 Exception。其中,Error是指程序無法處理的錯誤,表示運行應用程序中較嚴重問題;Exception是指程序本身可以處理的錯誤,具體可分為運行時異常(派生于 RuntimeException 的異常)和其他異常。
此外,從異常是否必須需要被處理的角度來看,異常又可分為不受檢查異常和受檢查異常兩種情況:
(1)不受檢查異常:派生于 Error 或 RuntimeException 的所有異常。
(2)受檢查異常:除去不受檢查異常的所有異常。
finally子句,在對應的try子句執行的前提下,finally 子句總會被執行。并且,finally子句 總是在諸如return、break、throw和continue等控制轉移語句之前執行。
13、六大設計原則
(1)單一職責原則:高內聚,一個類只做它該做的事情;
(2)接口隔離原則:接口小而專,避免大而全;
(3)依賴倒置原則:依賴抽象而非實現,面向接口編程;
(4)里氏替換原則:子類可以擴展父類的功能,但不能改變父類原有的功能;
(5)開閉原則:Open for Extension, Closed for Modification,例如AOP,代理模式,適配器模式就是其經典應用;
(6)迪米特法則:高內聚,低耦合;
14、代理模式
根據代理類的創建時機和創建方式的不同,我們可以將代理模式分為靜態代理和動態代理兩種形式,其中,在程序運行前就已經存在的編譯好的代理類是為靜態代理,在程序運行期間根據需要動態的創建代理類及其實例來完成具體的功能是為動態代理。其中,代理對象的作用如下:
(1) 代理對象存在的價值主要用于攔截對真實業務對象的訪問;
(2) 代理對象應該具有和目標對象(真實業務對象)相同的方法,即實現共同的接口或繼承于同一個類;
(3) 代理對象應該是目標對象的增強,否則我們就沒有必要使用代理了。
JDK 動態代理是動態代理模式的經典實現,主要包括三個角色對象:Subject (接口)、被代理的類以及InvocationHandler接口(一般持有被代理對象),例如:
(1)實現 InvocationHandler 接口
(2)Proxy.newProxyInstance
但是,JDK動態代理只能完成對接口的代理,而不能完成對類的代理,關鍵原因為:Java只允許單繼承。具體地,代理對象proxySubject的類型為“com.sun.proxy.$Proxy0”,這恰好印證了proxySubject對象是一個代理對象。除此之外,我們還發現代理對象proxySubject所對應的類繼承自java.lang.reflect.Proxy類,這也正是JDK動態代理機制無法實現對class的動態代理的原因。
15、迭代器模式
迭代器模式是與集合共生共死。一般來說,我們實現一個容器,就需要同時提供這個容器的迭代器,使用迭代器的好處:封裝容器內部的實現細節,對于不同的集合,可以提供統一的遍歷方式,簡化客戶端的訪問和獲取容器內數據。
特別需要注意的是,在迭代器模式中,具體迭代器角色和具體容器角色是耦合在一起的 —— 遍歷算法是與容器的內部細節緊密相關的。為了使客戶程序從與具體迭代器角色耦合的困境中脫離出來,避免具體迭代器角色的更換給客戶程序帶來的修改,迭代器模式抽象了具體迭代器角色,使得客戶程序更具一般性和重用性,這被稱為多態迭代。
在 Java Collection FrameWork中,提供的具體迭代器角色是定義在容器角色中的內部類,這樣便保護了容器的封裝。但是,同時容器也提供了遍歷算法接口,并且你可以擴展自己的迭代器。大家考慮一個問題,為什么一定要去實現 Iterable 這個接口呢? 為什么不直接實現 Iterator接口呢?
看一下 JDK 中的集合類,比如 List一族或者Set一族,都是實現了 Iterable 接口,但并不直接實現 Iterator 接口。仔細想一下這么做是有道理的:因為 Iterator接口的核心方法 next() 或者 hasNext() 是依賴于迭代器的當前迭代位置的。若 Collection 直接實現 Iterator 接口,勢必導致集合對象中包含當前迭代位置的數據(指針)。當集合在不同方法間被傳遞時,由于當前迭代位置不可預置,那么 next() 方法的結果會變成不可預知。除非再為 Iterator接口 添加一個 reset() 方法,用來重置當前迭代位置。但即使這樣,Collection 也只能同時存在一個當前迭代位置(不能同時多次迭代同一個序列:必須要等到當前次迭代完成并reset后,才能再一次從頭迭代)。 而選擇實現 Iterable 接口則不然,每次調用都會返回一個從頭開始計數的迭代器(Iterator),因此,多個迭代器間是互不干擾的。
16、適配器模式
適配器模式將一個類的接口轉換成客戶期望的另一個接口,讓原本不兼容的接口可以合作無間。也就是說,適配器模式用于實現新、老接口之間的轉換與適配,其魅力在于:不改變原有接口,卻還能使用新接口的功能。
適配器模式主要包含以下四個角色,其內涵分別為:
(1)Target: 客戶所期待的接口;
(2)Adaptee: Adapter 所包裝的對象,即被適配的類(適配者);
(3)Adapter: 一個用于包裝不兼容接口的對象的包裝類,通過包裝一個需要適配的對象,把原接口轉換成目標接口;
(4)Client:客戶端;
適配器模式的三個特點:
適配器對象實現原有接口;
適配器對象組合一個實現新接口的對象(這個對象也可以不實現一個接口,只是一個單純的對象);
對適配器原有接口方法的調用被委托給新接口的實例的特定方法。
1、模板方法模式
模板方法模式是一種基于繼承的代碼復用技術,是一種類行為型模式,其核心在于:定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
18、策略模式
策略模式屬于對象的行為模式,其用意是針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換,核心思想是:面向接口編程。
策略模式的經典應用包括Spring的PlatfromTransactionManager,JDK 排序策略 (不同的Comparator)等,其優點包括:
(1)算法可以自由切換,避免使用多重條件判斷;
(2)擴展性良好。
策略模式與模板方法的區別:
對于策略模式而言,一個“策略”是一個整體的(完整的)算法,算法是可以被整體替換的;而模板方法只能被替換其中的特定點,算法流程是固定不可變的。在思想和意圖上看,模板方法更加強調:
①定義一條線(算法流程),線上的多個點是可以變化的(具體實現在子類中完成),線上的多個點一定是會被執行的,并且一定是按照特定流程被執行的。
②算法流程是唯一的入口,對于點的訪問是受限的。
19、Java 自動裝箱、拆箱機制
Java為每種基本數據類型都提供了對應的包裝器類型。所謂自動裝箱機制就是自動將基本數據類型轉換為包裝器類型,而自動拆箱機制就是自動將包裝器類型轉換為基本數據類型。在JDK中,裝箱過程是通過調用包裝器的valueOf方法實現的,而拆箱過程是通過調用包裝器的 xxxValue方法實現的(xxx代表對應的基本數據類型)。但是,
(1)Integer、Short、Byte、Character、Long 這幾個類的valueOf方法的實現是類似的,共享[-128,127];
(2)Double、Float的valueOf方法的實現是類似的,無限不可列舉,不共享;
(3)Boolean的valueOf方法的實現不同于以上的整型和浮點型,只有兩個值,有限可列舉,共享;
什么時候裝箱/拆箱?
至于什么時候裝箱,什么時候拆箱主要取決于:在當前場景下,你需要的是引用類型還是原生類型。(例如,使用equals方法時傳進來原生類型的值);若需要的是原生類型,但傳進來的值是引用類型,則自動拆箱(例如,使用運算符進行運算時,操作數是包裝類型)。
20、內部類
內部類指的是在一個類的內部所定義的類,類名不需要和源文件名相同。在Java中,內部類是一個編譯時的概念,一旦編譯成功,內部類和外部類就會成為兩個完全不同的類,共有四種類型:
(1)成員內部類:成員內部類是外圍類的一個成員,是依附于外圍類的,所以,只有先創建了外圍類對象才能夠創建內部類對象。也正是由于這個原因,成員內部類也不能含有 static 的變量和方法;
(2)靜態內部類:靜態內部類,就是修飾為static的內部類,該內部類對象不依賴于外部類對象,就是說我們可以直接創建內部類對象,但其只可以直接訪問外部類的所有靜態成員和靜態方法;
(3)局部內部類:局部內部類和成員內部類一樣被編譯,只是它的作用域發生了改變,它只能在該方法和屬性中被使用,出了該方法和屬性就會失效;
(4)匿名內部類:定義匿名內部類的前提是,內部類必須要繼承一個類或者實現接口,格式為 new 父類或者接口(){定義子類的內容(如函數等)}。也就是說,匿名內部類最終提供給我們的是一個匿名子類的對象。
20.1內部類的作用
(1)間接實現多重繼承,例如:
(2)內部類還可以很好的實現隱藏(一般非內部類,是不允許有private與protected權限的),但內部類可以。
21、equals, hashCode, ==
(1)== 用于判斷兩個對象是否為同一個對象或者兩基本類型的值是否相等;
(2)equals 用于判斷兩個對象內容是否相同;
(3)hashCode是一個對象的消息摘要函數,一種壓縮映射,其一般與equals()方法同時重寫;若不重寫hashCode方法,默認使用Object類的hashCode方法,該方法是一個本地方法,由 Object 類定義的 hashCode 方法會針對不同的對象返回不同的整數。
21.1 equals與hashCode的區別
(1)一般來講,equals 這個方法是給用戶調用的,而 hashcode 方法一般用戶不會去調用;
(2)當一個對象類型作為集合對象的元素時,那么這個對象應該擁有自己的equals()和hashCode()設計,而且要遵守前面所說的幾個原則。
21.2 在HashMap中使用可變對象作為Key帶來的問題
HashMap用Key的哈希值來存儲和查找鍵值對,如果HashMap Key的哈希值在存儲鍵值對后發生改變,那么Map可能再也查找不到這個Entry了。也就是說,在HashMap中可變對象作為Key會造成數據丟失。因此,
(1)在HashMap中盡量使用不可變對象作為Key,比如,使用String、Integer等不可變類型用作Key是非常明智的或者使用自己定義的不可變類。
(2)如果可變對象在HashMap中被用作鍵,那就要小心在改變對象狀態的時候,不要改變它的哈希值了,例如,可以只根據對象的標識屬性生成HashCode。
21.3 重新equals但不重寫HashCode會出現的問題
在使用Set時,若向其加入兩個相同(equals返回為true)的對象,由于hashCode函數沒有進行重寫,那么這兩個對象的hashCode值必然不同,它們很有可能被分散到不同的桶中,容易造成重復對象
的存在。
22、什么是不可變對象
一個不可變對象應該滿足以下幾個條件:
(1)基本類型變量的值不可變;
(2)引用類型變量不能指向其他對象;
(3)引用類型所指向的對象的狀態不可變;
(4)除了構造函數之外,不應該有其它任何函數(至少是任何public函數)修改任何成員變量;
(5)任何使成員變量獲得新值的函數都應該將新的值保存在新的對象中,而保持原來的對象不被修改。
23、Java的序列化/反序列化機制
使用Serializable序列化/反序列化。將實現了Serializable接口的對象轉換成一個字節序列,并能夠在以后將這個字節序列完全恢復為原來的對象,序列化可以彌補不同操作系統之間的差異。其中,需要注意以下幾點:
(1)需要序列化的對象必須實現Serializable接口;
(2)只有非靜態字段和非transient字段進行序列化,與字段的可見性無關;
(3)序列化/反序列化的實質上操縱的是一個對象圖;
此外,Java中常用到的序列化方法還有 XML、JSON 等,此不贅述。
24、Path及ClassPath環境變量
(1)Path系統用來指定可執行文件的完整路徑。當在CMD中執行命令時,如果執行的可執行文件不在當前目錄下,那么系統就會依次搜索PATH中設置的路徑。如果在PATH中設置了JDK的安裝目錄(如在PATH中添加如下的路徑——D:ProgramFilesJavajdk1.8.0bin;D:ProgramFilesJavajdk1.8.0jrebin;),那么就可以在CMD中直接使用java、javac等命令,而不必在CMD中切換到JDK的安裝目錄下運行該命令。
(2)ClassPath是指定程序中所使用的類文件所在的位置。ClassPath環境變量中點(“.”)的含義是:在當前路徑下搜索Java類。如果編譯器按照CLASSPATH指定的路徑找不到所需要的類,則會提示“*類找不到”這樣的錯誤。






