瘋狂Java之學習筆記(25)-------------修飾符">瘋狂Java之學習筆記(25)-------------修飾符
1021
2025-03-31
瘋狂Java之學習筆記(17)---------------變量和權限
首先先總結一下變量的具體內容
Java變量的聲明在 Java 程序設計中,每個聲明的變量都必須分配一個類型。聲明一個變量時,應該先聲明變量的類型,隨后再聲明變量的名字。下面演示了變量的聲明方式。
double salary;
int age;
Boolean op;
其中第一項稱為變量類型,第二項稱為變量名。分號是必須的,這是 Java 語句的結束符號。
同一類型的不同變量,可以聲明在一行,也可以聲明在不同行,如果要聲明在同一行中,不同的變量之間用逗號分隔,例如下面的例子。
int studentNumber,people;
聲明變量的同時可以為變量賦值,也可以聲明以后再賦值。如:
int a=1; ?//聲明時賦值
int a; ? ? a=1; ? ?//聲明后賦值
注意:在程序運行過程中,空間內的值是變化的,這個內存空間就稱為變量。為了操作方便,給這個空間取了個名字,稱為變量名,內存空間內的值就是變量值。所以,申請了內存空間,變量不一定有值,要想變量有值,就必須要放入值。
例如:“int x”; 定義了變量但沒有賦值,即申請了內存空間,但沒有放入值;int x=5;?不但申請了內存空間而且還放入了值,值為 5。
注意:沒有賦值的變量,系統將按下列默認值進行初始化。
二.變量的作用域(全局變量|局部變量)
根據作用域(作用范圍)來分,一般將變量分為全局變量和局部變量。從字面上理解很簡單,全局變量就是在程序范圍之內都有效的變量,而局部變量就是在程序中的一部分內是有效的。
在Java中,全局變量就是在類的整個范圍之內,都有效的變量。而局部變量就是在類中某個方法函數內或某個子類內,有效的變量,下面將從實際程序代碼中慢慢的體會。
1.全局變量示例
public class var{ ///a 是全局變量
int a=10;
public static void main(String[] args){
var v=new var();
v.print();
}
void print(){
System.out.println("全局變量 a="+a);
}
}
運行結果:
全局變量 a=10
從以上例子可以看出,變量“a”的值在整個類中都有效。
2.局部變量示例
public class Math1{?///c 是局部變量
public static void main(String[] args){
Math1 v=new Math1();
System.out.println("這個是局部變量 c="+c);
}
void print(){
int c=20;
}
}
以上代碼在編譯時,會出現錯誤,就是找不到變量“c”。這說明變量“c”只在方法“print()”中起作用,在方法外就無法再調用。
從上述代碼中可以看出,如果一個變量在類中定義,那么這個變量就是全局變量;而在類中的方法、函數中定義的變量就是局部變量。
三.全局變量與局部變量的聲明
public class var{
byte x; short y; int z; long a; float b;
double c; char d; boolean e;
public static void main(String[] args){
var m=new var();
System.out.println(" 打印數據 x="+m.x);
System.out.println(" 打印數據 y="+m.y);
System.out.println(" 打印數據 z="+m.z);
System.out.println(" 打印數據 a="+m.a);
System.out.println(" 打印數據 b="+m.b);
System.out.println(" 打印數據 c="+m.c);
System.out.println(" 打印數據 d="+m.d);
System.out.println(" 打印數據 e="+m.e);
}
}
運行結果:
打印數據 x=0
打印數據 y=0
打印數據 z=0
打印數據 a=0
打印數據 b=0.0
打印數據 c=0.0
打印數據 d=
打印數據 e=false
public class var1{
void printnumber(){
byte x; short y; int z; long a;
float b; double c; char d; boolean e;
}
public static void main(String[] args){
var1 m=new var1();
System.out.println(" 打印數據 x="+m.x);
System.out.println(" 打印數據 y="+m.y);
System.out.println(" 打印數據 z="+m.z);
System.out.println(" 打印數據 a="+m.a);
System.out.println(" 打印數據 b="+m.b);
System.out.println(" 打印數據 c="+m.c);
System.out.println(" 打印數據 d="+m.d);
System.out.println(" 打印數據 e="+m.e);
}
)
這個程序段編譯時就會報錯,原因是所有局部變量都沒有初始化。從以上兩段程序代碼得出一個結果:全局變量可以不用進行初始化賦值工作,而局部變量必須要進行初始化賦值工作。
java的內存分配問題
在任何編程語言中,無論是基本類型還是引用類型,不論其作用域如何,都必須為其分配一定的內存空間,Java 語言也不例外,Java 的數據類型可以分為兩種:基本類型(變量持有數據本身的值)和引用類型(是某個對象的引用,而并非是對象本身);基本類型包括:boolean、float、double、int、long、short、byte以及char;在Java編程語言中除基本類型以外其余都是引用類型如:類類型、數組類型等。
在計算機內存中主要來自四個地方:heap segment(堆區)、stack segment(棧區)、codesegment(代碼區)、data segment(數據區);不同的地方存放不同數據:其中堆區主要存放Java程序運行時創建的所有引用類型都放在其中;棧區主要存放Java程序運行時所需的局部變量、方法的參數、對象的引用以及中間運算結果等數據;代碼區主要存放Java的代碼;數據區主要存放靜態變量及全局變量;以下結合實例來探討其具體機制。
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Test {
static int i = 10;
public static void main(String[] args) {
Student s1 = new Student(“feng”, 21);
}
}
當該程序運行起來后,其計算機內存分布大致如下:
對象在內部表示Java虛擬機規范并沒有規定其在堆中是如何表示的。對象的內部的表示會直接影響到堆區的設計以及垃圾收集器(GC)的設計。
Java在堆中的表示方法具體有兩種:
把堆分成兩個部分:一個句柄池,一個對象池;表示如下圖所示:
用對象指針直接指向一組數據,而該數據包括對象實例數據以及指向方法區中的數據的指針,具體如下圖所示:
小結:通過對對象內存分配的分析,來使讀者對Java的底層有一個比較理性的認識,從而進一步掌握Java的基礎知識。在深入了解了Java內存的分配以后,才能為以后編寫高質量的程序打下堅實的基礎,而且可以借鑒該思想來分析其它面向對象語言的內存分配問題。
在網上又找了一些關于內存分配的資料與大家分享一下!寫的很詳細!
堆棧
靜態存儲區域
一個由C/C++編譯的程序占用的內存分為以下幾個部分
1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。
2、堆區(heap)— 由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收。注意它與數據結構中的堆是兩回事,分配方式倒是類似于鏈表。
3、全局區(靜態區)(static)— 全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束后由系統釋放。
4、文字常量區 — 常量字符串就是放在這里的,程序結束后由系統釋放 。
5、程序代碼區 — 存放函數體的二進制代碼。
Java中保存地址:
寄存器register?這是速度最快的地方 數據位于和其他所有方式都不同的一個地方 處理器的內部 不過 寄存器的數量十分有限 所以寄存器是根據需要由編譯器分配我們對此沒有直接的控制權 也不可能在自己的程序里找到寄存器存在的任何跡象。
JVM的寄存器用來存放當前系統狀態。然而,基于移植性要求,JVM擁有的寄存器數目不能過多。否則,對于任何本身的寄存器個數小于JVM的移植目標機,要用常規存儲來模擬高速寄存器,是比較困難的。同時JVM是基于棧(Stack)的,這也使得它擁有的寄存器較少。
JVM的寄存器包括下面四個:
(1)PC程序計數寄存器
(2)optop操作數棧棧頂地址寄存器。
(3)frame當前執行環境地址寄存器。
(4)vars局部變量首地址寄存器。
這些寄存器長度均為32位。其中PC用來記錄程序執行步驟,其余optop,frame,vars都存放JVM棧中對應地址,用來快速獲取當前執行所需的信息。
堆棧stack?堆棧位于常規 RAM 隨機訪問存儲器 內 但可通過它的 堆棧指針 獲得處理器的直接支持 堆棧指針若向下移 會創建新的內存 若向上移則會釋放那些內存這是一種特別快 特別有效的數據保存方式 僅次于寄存器 創建程序時 Java編譯器必須準確地知道堆棧內保存的所有數據的長度 以及 存在時間 這是由于它必須生成相應的代碼 以便向上和向下移動指針 這一限制無疑影響了程序的靈活性?所以盡管有些 Java數據要保存在堆棧里 特別是對象引用 但 Java 對象并不放到其中
在函數中定義的一些基本類型的變量和對象的引用變量都在函數的堆棧 中分配 。
當在一段代碼塊定義一個變量時,Java就在棧中為這個變量分配內存空間,當超過變量的作用域后,Java會自動釋放掉為該變量所分配的內存空間,該內存空間可以立即被另作他用。?所以盡量使用基本類型的變量.
堆(或 內存堆 heap)?一種常規用途的內存池 也在 RAM 內?所有 Java 對象都保存在里面?和堆棧不同 內存堆 或 堆 Heap 最吸引人的地方在于編譯器不必知道要從堆里分配多少存儲空間 也不必知道存儲的數據要在堆里呆多長的時間 因此用堆保存數據時會得到更大的靈活性 要創建一個對象時?只需用 new 命令編制相關的代碼即可執行這些代碼時 就會在堆里自動進行數據的保存?不過 為了獲得這種靈活性我們也必然需要付出一定的代價 假如在內存堆里分配存儲空間 和分配規格存儲空間相比 前者要花掉更長的時間 和 C++不同 Java 事實上是不允許在堆棧里創建對象的 這樣說 只是為了進行理論上的一種比較
堆內存用來存放由 new創建的對象和數組。?由Java虛擬機的自動垃圾回收器來管理。
在堆中產生了一個數組或對象后,還可以在棧中定義一個特殊的變量,讓棧中這個變量的取值等于數組或對象在堆內存中的首地址,棧中的這個變量就成了數組或對象的引用變量。
引用變量就相當于是為數組或對象起的一個名稱,以后就可以在程序中使用棧中的引用變量來訪問堆中的數組或對象
靜態存儲static storage? 靜態 Static 是指 位于固定位置 盡管仍在 RAM 里程序運行期間 靜態存儲的數據將隨時等候調用 可用 static 關鍵字指出一個對象的特定元素是靜態的 但 Java 對象本身永遠都不會不會置入靜態存儲空間
常數存儲constant storage?常數值通常直接置于程序代碼內部 這樣做是安全的 因為它們永遠都不會改變有的常數需要嚴格地保護 所以可考慮將它們置入只讀存儲器 ROM
非 RAM 存儲?若數據完全獨立于一個程序之外 那么即使程序不運行了 它們仍可存在 并處在程序的控制范圍之外 其中兩個最主要的例子便是?流式對象?和?持久性對象?對于流式對象 對象會變成字節流通常會發給另一臺機器 而對于持久性對象我們可把它們保存在磁盤或磁帶中 即使程序中止運行 它們仍可保持自己的狀態不變之所以要設計這些類型的數據存儲 最主要的一個考慮便是把對象變成可在其他媒體上存在的形式 以后一旦需要 還可重新變回一個普通的 存在于 RAM 里的對象 目前 Java 只提供了有限的 持久性對象 支持 在未來的 Java 版本中 有望提供對 持久性 更完善的支持。
棧與堆都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
Java的堆或者說內存堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由于要在運行時動態分配內存,存取速度較慢。
堆棧的優勢是,存取速度比堆要快,僅次于寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變量(,int, short, long, byte, float, double, boolean, char)和對象句柄。
棧有一個很重要的特殊性,就是存在棧中的數據可以共享 。?假設我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中創建一個變量為a的引用,然后查找棧中是否有3這個值,如果沒找到,就將3存放進來,然后將a指向3。接著處理int b = 3;在創建完b的引用變量后,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那么編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進來,并令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改并不會影響到b, 它是由編譯器完成的,它有利于節省空間。而一個對象引用變量修改了這個對象的內部狀態,會影響到另一個對象引用變量。
String是一 個特殊的包裝類數據。可以用:
String str = new String("abc");
String str = "abc";
兩種的形式來創建,第一種是用new()來新 建對象的,它會在存放于堆中。每調用一次就會創建一個新的對象。
->?String str = new String("abc");自己補充:應該說有會產生兩個對象,一個為new String("abc")的實體對象放到內存堆中, 一個為堆棧對象str 也就是類實例對象的引用對象。
而第二種(String str = "abc";)是先在棧中創建一個對String類的對象引用變量str,然后查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,并令str指向”abc”,如果已經有”abc” 則直接令str指向“abc”。
比較類里面的數值是否相等時,用equals()方法;當?測試兩個包裝類的引用是否指向同一個對象時,用==,?下面用例子說明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和 str2是指向同一個對象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的對象。每一次生成一個?。
因此用第二種方式(String str = "abc";)創建多個”abc”字符串,在內存中其實只存在一個對象而已. 這種寫法有利與節省內存空間. 同時它可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對于String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字符串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。
另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能并沒有被創建!而可能只是指向一個先前已經創建的對象。只有通過new()方法才能保證每次都創建一個新的對象。由于String類的immutable性質,當String變量需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。
(http://blog.csdn.net/gaowenming/archive/2010/02/22/5316423.aspx)資料借鑒
總結完畢!
封裝和隱藏
理解封裝
封裝(Encapsulation)是面向對象三大特征之一(封裝、繼承、多態),它指的是將對象的狀態信息隱藏在內部,不允許外部程序直接訪問對象內部信息,而是通過該類所提供的方法來實現對內部信息的操作和訪問。
對一個類或對象實現良好的封裝,可以實現以下目的:
μ隱藏類的實現細節。
μ讓使用者只能通過事先預定的方法訪問數據,從而可以在該方法里加入控制邏輯,限制對屬性不合理訪問。
μ可進行數據檢查,從而有利于保證對象信息的完整性。
μ便于修改,提高代碼的可維護性。
為了實現良好的封裝,需要從兩個方面考慮:
μ將對象的屬性和實現細節隱藏起來,不允許外部直接訪問。
μ把方法暴露出來,讓方法來操作或訪問這些屬性。
注意:對于類而言,可以使用public和默認訪問控制符修飾,使用public修飾的類可以被所有類使用,不使用任何訪問控制符修飾的類只能被同一個包中的所有類訪問。
定義Person類,實現良好的封裝。
public class Person{
private String name;
private int age;
public void setName(String name) {
//要求用戶名必須在2~6位之間
if (name.length() > 6 || name.length() < 2){
System.out.println("您設置的人名不符合要求");
}else{
this.name = name;
}
}
public String getName(){
return this.name;
}
public void setAge(int age){
//要求用戶年齡必須在0~100之間
if (age > 100 || age < 0){
System.out.println("您設置的年齡不合法");
}else{
this.age = age;
}
}
public int getAge(){
return this.age;
}
}
注意:屬性的getter方法和setter方法有重要的意義,命名應遵循的原則:將原屬性名的首字母大寫,并在前面分別增加set和get動詞,就變成setter和getter方法名 。
訪問控制符的使用總結:
μ類中絕大部分屬性應該使用 private修飾,除了一些static修飾的、類似全局變量的屬性,才考慮使用 public修飾。
μ有些方法只是用于輔助實現該類的其他方法,這些方法被稱為工具方法,也應用 private修飾。
μ如果某個類主要用作其他類的父類,該類里包含的大部分方法可能僅希望被其子類重寫,而不想被外界直接調用,則應該使用protected修飾這些方法。
μ希望暴露出來給其他類自由調用的方法使用public修飾。
μ頂級類通常都希望被其他類自由使用,所以大部分頂級類都使用public修飾。
ˉ package和import
μ 包:Java中,包(package)是一組相關的類和接口的集合。Java編譯器將包與文件系統的目錄一一對應起來。
μ
μ
優點:
ü避免大量類的重名沖突,擴大名字空間。
ü包體現了
封裝機制
。
μ 包的創建:如果希望把一個類放在指定的包結構下,應該在Java源程序的 第一個非注釋行放如下格式的代碼:package packageName[.packageName[…]];
μ
例4.18 包的創建。
package hbsi;
public class HelloWorld{
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
注意:
ü包名是有效地標識符即可,但從可讀性規范角度來看,包名應該全部由小寫字母組成。
ü為了避免不同公司之間類名的重復,Sun建議使用單位Internet域名倒寫來作為包名,
üpackage語句必須作為源文件的第一句非注釋性語句,一個源文件只能指定一個包,該源文件中可以定義多個類,則這些類將全部位于該包下。
ü如果沒有顯示指定package語句,則處于無名包下。實際企業開發中,通常不會把類定義在無名包下。
μ 包中類的使用
如果要使用包中的類,可以有兩種方法:
ü引用包中的類(使用類的全限定名稱)myPackage .mySubPackage . Book?bookObj=new myPackage .mySubPackage . Book();
üimport語句引入包中的類
格式:import? 包名 .類名;
或?????? import? 包名 .*;?????? //“*”號表示所有類
例如: import? myPackage .mySubPackage.*;
Book bookObj=new Book();
注意:在引入具有層次結構的包時,“*”號僅僅表示該包中的所有類,如果該包中還有子包,那么子包中的類時不被包括的。
μ比較兩種方法的優缺點:
ü適用包名作前綴的方法使程序清晰,很容易就看出所使用的類位于哪個包中;而引入包的方法要知道某個類所在的包比較困難.
ü使用引入包的方法會帶來名字沖突的問題,而使用包名作前綴不會存在這樣的問題.
ü使用包名作前綴書寫程序時比較麻煩.
μ靜態導入
JDK1.5以后更是增加了一種靜態導入的語法,它用于導入指定類的某個靜態屬性值或全部靜態屬性值。
ü導入指定類單個靜態屬性
import static 父包.子包…類名.靜態屬性名;
例:import static java.lang.System.out;
ü導入指定類全部靜態屬性
import static 父包.子包…類名.*;
例:import static java.lang.Math.*;
隱藏的實現
在面向對象的設計中,最關鍵的問題就是”將會變與不會變的東西分離開來”。
有時為了不讓客戶程序員修改他們不該修改的東西,有時為了讓自己修改代碼之后不會讓客戶程序出現問題,就必須設置一些控制訪問符,來限定各自的訪問范圍。
Java 中的范圍控制符有4個,分別是private、package(默認的范圍)、protected、public,
權限范圍由小到大。
方法及屬性的訪問控制
Private的訪問范圍是只有在本類中才可以訪問
Package的訪問范圍是在本package中的所有類都可以訪問
Protected的訪問范圍是該類的子類,及和它在同一package中的類可以訪問
Public在任何地方都可以訪問
類的訪問控制
類的訪問權限只能是package(默認)和public的
為了控制不讓別人隨便訪問這個類,可以通過將這個類的構造函數設為private,這樣就只有你就沒有人可以創建這個對象啦。
你可以用一個static(靜態)方法創建對象。
實例:
public? class Sample {
private Sample (){
System.out.println("create a Sample ");
}
public? static Sample get Sample (){
return new Sample ();
}
}
或者可以先定義創建一個private的static的對象,再通過一個public的static方法返回這個對象的引用,這樣做到話可以實現 singleton(單例)模式,只會創建一個對象。
實例:
public? class Sample {
private static Sample s1 = new Sample ();
public static Sample getSample(){
return s1;
}}
隱藏與覆蓋的區別
關于隱藏和覆蓋的區別,要提到RTTI(run-time type identification)(運行期類型檢查),也就是運行期的多態,當一個父類引用指向子類對象的時候,請看下面我編寫的一段代碼:
代碼如下:
public class RunTime {
public static void main(String[] args) {
Animal a = new Cat();
System.out.println(a.A);
System.out.println(a.b);
a.voice();
a.method();
}
}
class Dog extends Animal {
public int b = 3;
public static int A = 3;
public static void method(){
System.out.println("狗");
}
public void voice() {
System.out.println("狗叫");
}
}
class Cat extends Animal {
public int b = 4;
public static int A = 4;
public static void method(){
System.out.println("貓");
}
public void voice() {
System.out.println("貓叫");
}
}
class Animal {
public int b = 0;
public static int A = 0;
public static void method(){
System.out.println("動物");
}
public void voice() {
System.out.println("動物叫");
}
}
輸出結果是:
0
0
貓叫
動物
轉載自:https://blog.csdn.net/u011225629/article/details/45309955
Java JVM
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。