說起內(nèi)部類這個(gè)詞,想必很多人都不陌生,但是又會(huì)覺得不熟悉。原因是平時(shí)編寫代碼時(shí)可能用到的場景不多,用得最多的是在有事件監(jiān)聽的情況下,并且即使用到也很少去總結(jié)內(nèi)部類的用法。今天我們就來一探究竟。
一.內(nèi)部類基礎(chǔ)
在Java中,可以將一個(gè)類定義在另一個(gè)類里面或者一個(gè)方法里面,這樣的類稱為內(nèi)部類。廣泛意義上的內(nèi)部類一般來說包括這四種:成員內(nèi)部類、局部內(nèi)部類、匿名內(nèi)部類和靜態(tài)內(nèi)部類。下面就先來了解一下這四種內(nèi)部類的用法。
1.成員內(nèi)部類
成員內(nèi)部類是最普通的內(nèi)部類,它的定義為位于另一個(gè)類的內(nèi)部,形如下面的形式:
class Circle { double radius = 0; public Circle(double radius) { this.radius = radius; } class Draw { //內(nèi)部類 public void drawSahpe() { System.out.println("drawshape"); } } }
這樣看起來,類Draw像是類Circle的一個(gè)成員,Circle稱為外部類。成員內(nèi)部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態(tài)成員)。
class Circle { private double radius = 0; public static int count =1; public Circle(double radius) { this.radius = radius; } class Draw { //內(nèi)部類 public void drawSahpe() { System.out.println(radius); //外部類的private成員 System.out.println(count); //外部類的靜態(tài)成員 } } }
不過要注意的是,當(dāng)成員內(nèi)部類擁有和外部類同名的成員變量或者方法時(shí),會(huì)發(fā)生隱藏現(xiàn)象,即默認(rèn)情況下訪問的是成員內(nèi)部類的成員。如果要訪問外部類的同名成員,需要以下面的形式進(jìn)行訪問:
外部類.this.成員變量
外部類.this.成員方法
雖然成員內(nèi)部類可以無條件地訪問外部類的成員,而外部類想訪問成員內(nèi)部類的成員卻不是這么隨心所欲了。在外部類中如果要訪問成員內(nèi)部類的成員,必須先創(chuàng)建一個(gè)成員內(nèi)部類的對象,再通過指向這個(gè)對象的引用來訪問:
class Circle { private double radius = 0; public Circle(double radius) { this.radius = radius; getDrawInstance().drawSahpe(); //必須先創(chuàng)建成員內(nèi)部類的對象,再進(jìn)行訪問 } private Draw getDrawInstance() { return new Draw(); } class Draw { //內(nèi)部類 public void drawSahpe() { System.out.println(radius); //外部類的private成員 } } }
成員內(nèi)部類是依附外部類而存在的,也就是說,如果要?jiǎng)?chuàng)建成員內(nèi)部類的對象,前提是必須存在一個(gè)外部類的對象。創(chuàng)建成員內(nèi)部類對象的一般方式如下:
public class Test { public static void main(String[] args) { //第一種方式: Outter outter = new Outter(); Outter.Inner inner = outter.new Inner(); //必須通過Outter對象來創(chuàng)建 //第二種方式: Outter.Inner inner1 = outter.getInnerInstance(); } } class Outter { private Inner inner = null; public Outter() { } public Inner getInnerInstance() { if(inner == null) inner = new Inner(); return inner; } class Inner { public Inner() { } } }
內(nèi)部類可以擁有private訪問權(quán)限、protected訪問權(quán)限、public訪問權(quán)限及包訪問權(quán)限。比如上面的例子,如果成員內(nèi)部類Inner用private修飾,則只能在外部類的內(nèi)部訪問,如果用public修飾,則任何地方都能訪問;
如果用protected修飾,則只能在同一個(gè)包下或者繼承外部類的情況下訪問;如果是默認(rèn)訪問權(quán)限,則只能在同一個(gè)包下訪問。這一點(diǎn)和外部類有一點(diǎn)不一樣,外部類只能被public和包訪問兩種權(quán)限修飾。
2.局部內(nèi)部類
局部內(nèi)部類是定義在一個(gè)方法或者一個(gè)作用域里面的類,它和成員內(nèi)部類的區(qū)別在于局部內(nèi)部類的訪問僅限于方法內(nèi)或者該作用域內(nèi)。
class People{ public People() { } } class Man{ public Man(){ } public People getWoman(){ class Woman extends People{ //局部內(nèi)部類 int age =0; } return new Woman(); } }
注意,局部內(nèi)部類就像是方法里面的一個(gè)局部變量一樣,是不能有public、protected、private以及static修飾符的。
3.匿名內(nèi)部類
匿名內(nèi)部類應(yīng)該是平時(shí)我們編寫代碼時(shí)用得最多的,在編寫事件監(jiān)聽的代碼時(shí)使用匿名內(nèi)部類不但方便,而且使代碼更加容易維護(hù)。下面這段代碼是一段Android事件監(jiān)聽代碼:
scan_bt.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub } }); history_bt.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub } });
這段代碼為兩個(gè)按鈕設(shè)置監(jiān)聽器,這里面就使用了匿名內(nèi)部類。這段代碼中的:
new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub } }
就是匿名內(nèi)部類的使用。代碼中需要給按鈕設(shè)置監(jiān)聽器對象,使用匿名內(nèi)部類能夠在實(shí)現(xiàn)父類或者接口中的方法情況下同時(shí)產(chǎn)生一個(gè)相應(yīng)的對象,但是前提是這個(gè)父類或者接口必須先存在才能這樣使用。當(dāng)然像下面這種寫法也是可以的,跟上面使用匿名內(nèi)部類達(dá)到效果相同。
private void setListener() { scan_bt.setOnClickListener(new Listener1()); history_bt.setOnClickListener(new Listener2()); } class Listener1 implements View.OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub } } class Listener2 implements View.OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub } }
這種寫法雖然能達(dá)到一樣的效果,但是既冗長又難以維護(hù),所以一般使用匿名內(nèi)部類的方法來編寫事件監(jiān)聽代碼。同樣的,匿名內(nèi)部類也是不能有訪問修飾符和static修飾符的。
匿名內(nèi)部類是唯一一種沒有構(gòu)造器的類。正因?yàn)槠錄]有構(gòu)造器,所以匿名內(nèi)部類的使用范圍非常有限,大部分匿名內(nèi)部類用于接口回調(diào)。匿名內(nèi)部類在編譯的時(shí)候由系統(tǒng)自動(dòng)起名為Outter$1.class。
一般來說,匿名內(nèi)部類用于繼承其他類或是實(shí)現(xiàn)接口,并不需要增加額外的方法,只是對繼承方法的實(shí)現(xiàn)或是重寫。
4.靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類也是定義在另一個(gè)類里面的類,只不過在類的前面多了一個(gè)關(guān)鍵字static。靜態(tài)內(nèi)部類是不需要依賴于外部類的,這點(diǎn)和類的靜態(tài)成員屬性有點(diǎn)類似,并且它不能使用外部類的非static成員變量或者方法,這點(diǎn)很好理解,因?yàn)樵跊]有外部類的對象的情況下,可以創(chuàng)建靜態(tài)內(nèi)部類的對象,如果允許訪問外部類的非static成員就會(huì)產(chǎn)生矛盾,因?yàn)橥獠款惖姆莝tatic成員必須依附于具體的對象。
public class Test { public static void main(String[] args) { Outter.Inner inner = new Outter.Inner(); } } class Outter { public Outter() { } static class Inner { public Inner() { } } }
二.深入理解內(nèi)部類
1.為什么成員內(nèi)部類可以無條件訪問外部類的成員?
在此之前,我們已經(jīng)討論過了成員內(nèi)部類可以無條件訪問外部類的成員,那具體究竟是如何實(shí)現(xiàn)的呢?下面通過反編譯字節(jié)碼文件看看究竟。事實(shí)上,編譯器在進(jìn)行編譯的時(shí)候,會(huì)將成員內(nèi)部類單獨(dú)編譯成一個(gè)字節(jié)碼文件,下面是Outter.java的代碼:
public class Outter { private Inner inner = null; public Outter() { } public Inner getInnerInstance() { if(inner == null) inner = new Inner(); return inner; } protected class Inner { public Inner() { } } }
編譯之后,出現(xiàn)了兩個(gè)字節(jié)碼文件:
反編譯Outter$Inner.class文件得到下面信息:
E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$Inner Compiled from "Outter.java" public class com.cxh.test2.Outter$Inner extends java.lang.Object SourceFile: "Outter.java" InnerClass: #24= #1 of #22; //Inner=class com/cxh/test2/Outter$Inner of class com/cxh/tes t2/Outter minor version: 0 major version: 50 Constant pool: const #1 = class #2; // com/cxh/test2/Outter$Inner const #2 = Asciz com/cxh/test2/Outter$Inner; const #3 = class #4; // java/lang/Object const #4 = Asciz java/lang/Object; const #5 = Asciz this$0; const #6 = Asciz Lcom/cxh/test2/Outter;; const #7 = Asciz <init>; const #8 = Asciz (Lcom/cxh/test2/Outter;)V; const #9 = Asciz Code; const #10 = Field #1.#11; // com/cxh/test2/Outter$Inner.this$0:Lcom/cxh/t est2/Outter; const #11 = NameAndType #5:#6;// this$0:Lcom/cxh/test2/Outter; const #12 = Method #3.#13; // java/lang/Object."<init>":()V const #13 = NameAndType #7:#14;// "<init>":()V const #14 = Asciz ()V; const #15 = Asciz LineNumberTable; const #16 = Asciz LocalVariableTable; const #17 = Asciz this; const #18 = Asciz Lcom/cxh/test2/Outter$Inner;; const #19 = Asciz SourceFile; const #20 = Asciz Outter.java; const #21 = Asciz InnerClasses; const #22 = class #23; // com/cxh/test2/Outter const #23 = Asciz com/cxh/test2/Outter; const #24 = Asciz Inner; { final com.cxh.test2.Outter this$0; public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: putfield #10; //Field this$0:Lcom/cxh/test2/Outter; 5: aload_0 6: invokespecial #12; //Method java/lang/Object."<init>":()V 9: return LineNumberTable: line 16: 0 line 18: 9 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/cxh/test2/Outter$Inner; }
第11行到35行是常量池的內(nèi)容,下面逐一第38行的內(nèi)容:
final com.cxh.test2.Outter this$0;
這行是一個(gè)指向外部類對象的指針,看到這里想必大家豁然開朗了。也就是說編譯器會(huì)默認(rèn)為成員內(nèi)部類添加了一個(gè)指向外部類對象的引用,那么這個(gè)引用是如何賦初值的呢?下面接著看內(nèi)部類的構(gòu)造器:
public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);
從這里可以看出,雖然我們在定義的內(nèi)部類的構(gòu)造器是無參構(gòu)造器,編譯器還是會(huì)默認(rèn)添加一個(gè)參數(shù),該參數(shù)的類型為指向外部類對象的一個(gè)引用,所以成員內(nèi)部類中的Outter this&0 指針便指向了外部類對象,因此可以在成員內(nèi)部類中隨意訪問外部類的成員。
從這里也間接說明了成員內(nèi)部類是依賴于外部類的,如果沒有創(chuàng)建外部類的對象,則無法對Outter this&0引用進(jìn)行初始化賦值,也就無法創(chuàng)建成員內(nèi)部類的對象了。
2.為什么局部內(nèi)部類和匿名內(nèi)部類只能訪問局部final變量?
想必這個(gè)問題也曾經(jīng)困擾過很多人,在討論這個(gè)問題之前,先看下面這段代碼:
public class Test { public static void main(String[] args) { } public void test(final int b) { final int a = 10; new Thread(){ public void run() { System.out.println(a); System.out.println(b); }; }.start(); } }
這段代碼會(huì)被編譯成兩個(gè)class文件:Test.class和Test1.class。默認(rèn)情況下,編譯器會(huì)為匿名內(nèi)部類和局部內(nèi)部類起名為Outter1.class。默認(rèn)情況下,編譯器會(huì)為匿名內(nèi)部類和局部內(nèi)部類起名為Outterx.class(x為正整數(shù))。
根據(jù)上圖可知,test方法中的匿名內(nèi)部類的名字被起為 Test$1。
上段代碼中,如果把變量a和b前面的任一個(gè)final去掉,這段代碼都編譯不過。我們先考慮這樣一個(gè)問題:
當(dāng)test方法執(zhí)行完畢之后,變量a的生命周期就結(jié)束了,而此時(shí)Thread對象的生命周期很可能還沒有結(jié)束,那么在Thread的run方法中繼續(xù)訪問變量a就變成不可能了,但是又要實(shí)現(xiàn)這樣的效果,怎么辦呢?Java采用了 復(fù)制 的手段來解決這個(gè)問題。將這段代碼的字節(jié)碼反編譯可以得到下面的內(nèi)容:
我們看到在run方法中有一條指令:
bipush 10
這條指令表示將操作數(shù)10壓棧,表示使用的是一個(gè)本地局部變量。這個(gè)過程是在編譯期間由編譯器默認(rèn)進(jìn)行,如果這個(gè)變量的值在編譯期間可以確定,則編譯器默認(rèn)會(huì)在匿名內(nèi)部類(局部內(nèi)部類)的常量池中添加一個(gè)內(nèi)容相等的字面量或直接將相應(yīng)的字節(jié)碼嵌入到執(zhí)行字節(jié)碼中。這樣一來,匿名內(nèi)部類使用的變量是另一個(gè)局部變量,只不過值和方法中局部變量的值相等,因此和方法中的局部變量完全獨(dú)立開。
下面再看一個(gè)例子:
public class Test { public static void main(String[] args) { } public void test(final int a) { new Thread(){ public void run() { System.out.println(a); }; }.start(); } }
反編譯得到:
我們看到匿名內(nèi)部類Test$1的構(gòu)造器含有兩個(gè)參數(shù),一個(gè)是指向外部類對象的引用,一個(gè)是int型變量,很顯然,這里是將變量test方法中的形參a以參數(shù)的形式傳進(jìn)來對匿名內(nèi)部類中的拷貝(變量a的拷貝)進(jìn)行賦值初始化。
也就說如果局部變量的值在編譯期間就可以確定,則直接在匿名內(nèi)部里面創(chuàng)建一個(gè)拷貝。如果局部變量的值無法在編譯期間確定,則通過構(gòu)造器傳參的方式來對拷貝進(jìn)行初始化賦值。
從上面可以看出,在run方法中訪問的變量a根本就不是test方法中的局部變量a。這樣一來就解決了前面所說的 生命周期不一致的問題。
但是新的問題又來了,既然在run方法中訪問的變量a和test方法中的變量a不是同一個(gè)變量,當(dāng)在run方法中改變變量a的值的話,會(huì)出現(xiàn)什么情況?
對,會(huì)造成數(shù)據(jù)不一致性,這樣就達(dá)不到原本的意圖和要求。為了解決這個(gè)問題,java編譯器就限定必須將變量a限制為final變量,不允許對變量a進(jìn)行更改(對于引用類型的變量,是不允許指向新的對象),這樣數(shù)據(jù)不一致性的問題就得以解決了。
到這里,想必大家應(yīng)該清楚為何 方法中的局部變量和形參都必須用final進(jìn)行限定了。
3.靜態(tài)內(nèi)部類有特殊的地方嗎?
從前面可以知道,靜態(tài)內(nèi)部類是不依賴于外部類的,也就說可以在不創(chuàng)建外部類對象的情況下創(chuàng)建內(nèi)部類的對象。另外,靜態(tài)內(nèi)部類是不持有指向外部類對象的引用的,這個(gè)讀者可以自己嘗試反編譯class文件看一下就知道了,是沒有Outter this&0引用的。
三.內(nèi)部類的使用場景和好處
為什么在Java中需要內(nèi)部類?總結(jié)一下主要有以下四點(diǎn):
1.每個(gè)內(nèi)部類都能獨(dú)立的繼承一個(gè)接口的實(shí)現(xiàn),所以無論外部類是否已經(jīng)繼承了某個(gè)(接口的)實(shí)現(xiàn),對于內(nèi)部類都沒有影響。內(nèi)部類使得多繼承的解決方案變得完整,
2.方便將存在一定邏輯關(guān)系的類組織在一起,又可以對外界隱藏。
3.方便編寫事件驅(qū)動(dòng)程序
4.方便編寫線程代碼
四.常見的與內(nèi)部類相關(guān)的筆試面試題
1.根據(jù)注釋填寫(1),(2),(3)處的代碼
public class Test{ public static void main(String[] args){ // 初始化Bean1 (1) bean1.I++; // 初始化Bean2 (2) bean2.J++; //初始化Bean3 (3) bean3.k++; } class Bean1{ public int I = 0; } static class Bean2{ public int J = 0; } } class Bean{ class Bean3{ public int k = 0; } }
從前面可知,對于成員內(nèi)部類,必須先產(chǎn)生外部類的實(shí)例化對象,才能產(chǎn)生內(nèi)部類的實(shí)例化對象。而靜態(tài)內(nèi)部類不用產(chǎn)生外部類的實(shí)例化對象即可產(chǎn)生內(nèi)部類的實(shí)例化對象。
創(chuàng)建靜態(tài)內(nèi)部類對象的一般形式為: 外部類類名.內(nèi)部類類名 xxx = new 外部類類名.內(nèi)部類類名()
創(chuàng)建成員內(nèi)部類對象的一般形式為: 外部類類名.內(nèi)部類類名 xxx = 外部類對象名.new 內(nèi)部類類名()
因此,(1),(2),(3)處的代碼分別為:
Test test = new Test(); Test.Bean1 bean1 = test.new Bean1();
Test.Bean2 b2 = new Test.Bean2();
Bean bean = new Bean(); Bean.Bean3 bean3 = bean.new Bean3();
2.下面這段代碼的輸出結(jié)果是什么?
public class Test { public static void main(String[] args) { Outter outter = new Outter(); outter.new Inner().print(); } } class Outter { private int a = 1; class Inner { private int a = 2; public void print() { int a = 3; System.out.println("局部變量:" + a); System.out.println("內(nèi)部類變量:" + this.a); System.out.println("外部類變量:" + Outter.this.a); } } }
3
2
1
最后補(bǔ)充一點(diǎn)知識:關(guān)于成員內(nèi)部類的繼承問題。一般來說,內(nèi)部類是很少用來作為繼承用的。但是當(dāng)用來繼承的話,要注意兩點(diǎn):
1)成員內(nèi)部類的引用方式必須為 Outter.Inner.
2)構(gòu)造器中必須有指向外部類對象的引用,并通過這個(gè)引用調(diào)用super()。
class WithInner { class Inner{ } } class InheritInner extends WithInner.Inner { // InheritInner() 是不能通過編譯的,一定要加上形參 InheritInner(WithInner wi) { wi.super(); //必須有這句調(diào)用 } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner obj = new InheritInner(wi); } }
更多java知識請關(guān)注java基礎(chǔ)教程欄目。
Atas ialah kandungan terperinci java內(nèi)部類詳解(附相關(guān)面試題). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undress AI Tool
Gambar buka pakaian secara percuma

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Untuk mengendalikan transaksi JDBC dengan betul, anda mesti terlebih dahulu mematikan mod komit automatik, kemudian melakukan pelbagai operasi, dan akhirnya melakukan atau mengembalikan semula hasilnya; 1. Panggil Conn.SetAutOcommit (palsu) untuk memulakan transaksi; 2. Melaksanakan pelbagai operasi SQL, seperti memasukkan dan mengemaskini; 3. Panggil Conn.Commit () jika semua operasi berjaya, dan hubungi conn.rollback () jika pengecualian berlaku untuk memastikan konsistensi data; Pada masa yang sama, cuba-dengan-sumber harus digunakan untuk menguruskan sumber, mengendalikan pengecualian dengan betul dan menutup sambungan untuk mengelakkan kebocoran sambungan; Di samping itu, adalah disyorkan untuk menggunakan kolam sambungan dan menetapkan mata simpan untuk mencapai rollback separa, dan menyimpan urus niaga sesingkat mungkin untuk meningkatkan prestasi.

Gunakan kelas dalam pakej Java.Time untuk menggantikan kelas lama dan kelas kalendar; 2. Dapatkan tarikh dan masa semasa melalui LocalDate, LocalDateTime dan Tempatan Tempatan; 3. Buat tarikh dan masa tertentu menggunakan kaedah (); 4. Gunakan kaedah tambah/tolak untuk meningkatkan dan mengurangkan masa; 5. Gunakan zoneddatetime dan zonid untuk memproses zon waktu; 6. Format dan parse date string melalui DateTimeFormatter; 7. Gunakan segera untuk bersesuaian dengan jenis tarikh lama apabila perlu; pemprosesan tarikh di java moden harus memberi keutamaan untuk menggunakan java.timeapi, yang memberikan jelas, tidak berubah dan linear

Pra-formancetartuptimemoryusage, quarkusandmicronautleadduetocompile-timeprocessingandgraalvsupport, withquarkusoftenperforminglightbetterine serverless scenarios.tyvelopecosyste,

NetworkPortsandFireWallSworkTogethertoenableCommunicationWileensuringsecurity.1.networkportsarevirtualendpointsNumbered0-655 35, Withwell-KnownportsLike80 (http), 443 (https), 22 (ssh), dan25 (smtp) identitispecificservices.2.portsoperateovertcp (boleh dipercayai, c

Koleksi Sampah Java (GC) adalah mekanisme yang secara automatik menguruskan ingatan, yang mengurangkan risiko kebocoran ingatan dengan menuntut semula objek yang tidak dapat dicapai. 1.GC menghakimi kebolehcapaian objek dari objek akar (seperti pembolehubah stack, benang aktif, medan statik, dan lain -lain), dan objek yang tidak dapat dicapai ditandakan sebagai sampah. 2. Berdasarkan algoritma penandaan tanda, tandakan semua objek yang dapat dicapai dan objek yang tidak ditandai. 3. Mengamalkan strategi pengumpulan generasi: Generasi Baru (Eden, S0, S1) sering melaksanakan MinorGC; Orang tua melakukan kurang tetapi mengambil masa lebih lama untuk melakukan MajorGC; Metaspace Stores Metadata kelas. 4. JVM menyediakan pelbagai peranti GC: SerialGC sesuai untuk aplikasi kecil; ParallelGC meningkatkan throughput; CMS mengurangkan

GradleisthebetterChoiceFormostNewProjectSduetoitSsuperiorflexibility, Prestasi, danModernToolingSupport.1.Gradle'sGroovy/KOT lindslismoreconciseandexpressivethanmaven'sverbosexml.2.GradleOutPerformsMaveninBuildSpeedWithIncrementalcompilation, BuildCac

Defer digunakan untuk melaksanakan operasi tertentu sebelum fungsi pulangan, seperti sumber pembersihan; Parameter dinilai dengan serta-merta apabila menangguhkan, dan fungsi-fungsi dilaksanakan mengikut urutan terakhir (LIFO); 1. Pelbagai penahanan dilaksanakan dalam urutan terbalik pengisytiharan; 2. Biasanya digunakan untuk pembersihan yang selamat seperti penutupan fail; 3. Nilai pulangan yang dinamakan boleh diubah suai; 4. Ia akan dilaksanakan walaupun panik berlaku, sesuai untuk pemulihan; 5. Elakkan penyalahgunaan menangguhkan gelung untuk mengelakkan kebocoran sumber; Penggunaan yang betul boleh meningkatkan keselamatan kod dan kebolehbacaan.

Memilih jenis htmlinput yang betul dapat meningkatkan ketepatan data, meningkatkan pengalaman pengguna, dan meningkatkan kebolehgunaan. 1. Pilih jenis input yang sepadan mengikut jenis data, seperti teks, e -mel, tel, nombor dan tarikh, yang secara automatik boleh menyemak dan menyesuaikan diri dengan papan kekunci; 2. Gunakan HTML5 untuk menambah jenis baru seperti URL, Warna, Julat dan Carian, yang dapat memberikan kaedah interaksi yang lebih intuitif; 3. Gunakan pemegang tempat dan sifat -sifat yang diperlukan untuk meningkatkan kecekapan dan ketepatan pengisian bentuk, tetapi harus diperhatikan bahawa pemegang tempat tidak dapat menggantikan label.
