Java多型+構造方法+垃圾回收+內部類+Lambda+裝箱列舉註解
文章目錄
1 . 變數及其傳遞
1.1 基本型別變數與引用型變數
基本型別(primitive type):其值直接存於變數中。“在這裡”
引用型(reference type) 的變數(class,interface,陣列)除佔據一定的記憶體空間外,它所引用 的物件實體(由new 建立)也要佔據一定空間。“在那裡”
引用變數在這裡只是存一個物件實體的地址,通過這個引用能夠操作這個物件
MyDate m,n;
m=new MyDate();
n=m;
n.addYear();
.
public class MyDate {
private int day;
private int month;
private int year;
public MyDate(int y, int m, int d) {
year = y;
month = m;
day = d;
}
void addYear(){
year ++;
}
public void display() {
System.out.println(year + "-" + month + "-" +day);
}
public static void main(String[] args) {
MyDate m = new MyDate(2003, 9, 22);
MyDate n = m;//複製只是複製了一個引用
n.addYear();
m.display();//操作的是同一個物件
n.display();
}
}
1.2 欄位變數與區域性變數
欄位變數(field)與區域性變數(Local variable)
前者是在類中,後者是方法中定義的變數或方法的參變數
從記憶體角度看
儲存位置,欄位變數為物件的一部分、存在於堆中的,區域性變數是存在於棧中。
生命週期不同 欄位變數隨著物件的存在而存在,區域性變數隨著方法的存在而存在,隨著方法的結束而結束
初始值:欄位變數可以自動賦初值,區域性變數則須顯式賦值
class Test() {
int a;
void m(){
int b;
System.out.println(b);
//編譯不能通過需要初始化。
}
}
從語法角度看
欄位變數屬於類,可以用public,private,static,final 修飾。
區域性變數不能夠被訪問控制符及static修飾
都可以被final修飾
1.2 變數的傳遞
呼叫物件方法時,要傳遞引數。在傳遞引數時,
Java 是值傳遞,即,是將表示式的值複製給形式引數。
對於引用型變數,傳遞的值是引用值,而不是複製物件實體
可以改變物件的屬性
1.3變數的返回
方法的返回:
返回基本型別。
返回引用型別。它就可以存取物件實體。
Object getNewObject()
{
Object obj=new Object();
return obj;
}
呼叫時:Object p= GetNewObject();
不定長引數
不定長引數(Variable length arguments),從JDK1.5開始
用省略號表示, 並且是最後一個引數
實際上Java當成一個數組
int sum( int … nums){
int s=0;
for(int n : nums) s+=n;
return s;
}
呼叫:sum(1,2,3,4);
又例如: public static void main( String…argv)
2 . 多型和虛方法呼叫
2.1多型
多型(Polymorphism)是指一個程式中相同的名字表示不同的含義的情況。
多型有兩種情形
編譯時多型:
過載(overload) (多個同名的不同方法)。
如 p.sayHello(); p.sayHello(“Wang”);
執行時多型:
覆蓋(override) (子類對父類方法進行覆蓋)
動態繫結(dynamic binding) ----虛方法呼叫(virtual method invoking)
在呼叫方法時,程式會正確地呼叫子類物件的方法。
多型的特點大大提高了程式的抽象程度和簡潔性
2.2上溯造型
上溯造型(upcasting)
是把派生型別當作基本型別處理
例子見下面部落格
https://blog.csdn.net/weijie_home/article/details/49105871
Person p = new Student();
void fun(Person p ){
…
}
fun(new Person());
2.3虛方法呼叫
什麼是虛方法?
https://blog.csdn.net/vop444/article/details/69525124#commentBox
虛方法例子:
https://blog.csdn.net/qq_32863631/article/details/79227859
用虛方法呼叫,可以實現執行時的多型!
子類過載了父類方法時,執行時
執行時系統根據呼叫該方法的例項的型別(傳進去的時student,那麼就呼叫student)來決定選擇哪個方法呼叫
所有的非final方法都會自動地進行動態繫結!
什麼是動態繫結?
https://blog.csdn.net/javamoo/article/details/78776150
虛方法呼叫示例
class TestStaticInvoke
{
static void doStuff( Shape s ){
s.draw();
}
public static void main( String [] args ){
Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuff(c);
doStuff(t);
doStuff(l);
Shape s = new Circle();
doStuff(s);
s.draw();
Circle c2 = new Circle();
c2.draw();
}
}
class Shape
{
void draw(){ System.out.println("Shape Drawing"); }
}
class Circle extends Shape
{
void draw(){ System.out.println("Draw Circle"); }
}
class Triangle extends Shape
{
void draw(){ System.out.println("Draw Three Lines"); }
}
class Line extends Shape
{
void draw(){ System.out.println("Draw Line"); }
}
//輸出:
//Draw Circle
//Draw Three Lines
//Draw Line
//Draw Circle
//Draw Circle
//Draw Circle
動態型別確定
變數 instanceof 型別
結果是boolean 值(實際就是這個型別或者是他的子型別,則返回true)
對實際型別進行判斷
例子:
class Instanceof
{
public static void main(String[] args)
{
Object [] things = new Object[3];//object所有類的父類
things[0] = new Integer(4);
things[1] = new Double(3.14);
things[2] = new String("2.09");
double s = 0;
for( int i=0; i<things.length; i++ ){
if( things[i] instanceof Integer )//看看是不是屬於這個型別
s += ((Integer)things[i]).intValue();//強制型別轉換
else if( things[i] instanceof Double )
s += ((Double)things[i]).doubleValue();
}
System.out.println("sum=" + s);
}
}
//輸出:sum=7.140000000000001
什麼情況不是虛方法呼叫
Java中,普通的方法是虛方法
(在呼叫過程中會根據實際的物件來決定方法的呼叫)
但static,private方法不是虛方法呼叫 (static是屬於類的,private是屬於這個類自己的)
static,private與虛方法編譯後用的指令是不同的
package text1;
public class JavaP3methods {
void f(){}
private void p(){}
static void s(){}
public static void main(String...argv){
JavaP3methods obj = new JavaP3methods();
obj.f();
obj.p();
obj.s();
}
}
反彙編程式碼:
Compiled from "JavaP3methods.java"
public class text1.JavaP3methods {
public text1.JavaP3methods();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
void f();
Code:
0: return
static void s();
Code:
0: return
public static void main(java.lang.String...);
Code:
0: new #1 // class text1/JavaP3methods
3: dup
4: invokespecial #19 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #20 // Method f:()V
12: aload_1
13: invokespecial #22 // Method p:()V
16: invokestatic #24 // Method s:()V
19: return
}
三種非虛的方法
static的方法,以宣告的型別為準,與例項型別無關
private方法子類看不見,也不會被虛化
final方法子類不能覆蓋,不存在虛化問題
class example
{
static void doStuff( Shape s ){
s.draw();
}
public static void main( String [] args ){
Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuff(c);
doStuff(t);
doStuff(l);
Shape s = new Circle();
doStuff(s);
s.draw();
Circle c2 = new Circle();//可見static是非虛的,只跟宣告的型別有關
c2.draw();
}
}
class Shape
{
static void draw(){ System.out.println("Shape Drawing"); }
}
class Circle extends Shape
{
static void draw(){ System.out.println("Draw Circle"); }
}
class Triangle extends Shape
{
static void draw(){ System.out.println("Draw Three Lines"); }
}
class Line extends Shape
{
static void draw(){ System.out.println("Draw Line"); }
}
//輸出:
//Shape Drawing
//Shape Drawing
//Shape Drawing
//Shape Drawing
//Shape Drawing
//Draw Circle
3 . 物件構造與初始化
3.1 構造方法(constructor)
物件都有構造方法
如果沒有,編譯器加一個default構造方法 (預設構造方法什麼都不幹)
抽象類也有構造方法,任何一個物件都需要構造
呼叫本類或父類的構造方法
this呼叫本類的其他構造方法。
super呼叫直接父類的構造方法
this或super要放在第一條語句,且只能夠有一條
如果沒有this及super,則編譯器自動加上super(),即呼叫直接父類 不帶引數的構造方法
因為必須令所有父類的構造方法都得到呼叫,否則整個物件的構建就 可能不正確。
例子:
class example2
{
public static void main(String[] args){
Person p = new Graduate();
}
}
class Person
{
String name;
int age;
Person(){}
Person( String name, int age ){
this.name=name; this.age=age;
System.out.println("In Person(String,int)");
}
}
class Student extends Person
{
String school;
Student(){
this( null, 0, null );
System.out.println("In Student()");
}
Student( String name, int age, String school ){
super( name, age );
this.school = school;
System.out.println("In Student(String,int,String)");
}
}
class Graduate extends Student
{
String teacher="";
Graduate(){
//super();
System.out.println("In Graduate()");
}
}
//輸出結果:
//In Person(String,int)
//In Student(String,int,String)
//In Student()
//In Graduate()
上面程式碼可見雖然只寫了一個new,但是它是一直呼叫父類的構造方法,直到object
class A
{
A(int a){}
}
class B extends A
{
B(String s){} //編譯不能夠通過.
}
編譯器會自動呼叫B(String s){ super();} 他會呼叫不帶引數的構造方法,但是父類沒有不帶引數的,所以出錯.
解決方法:
在B的構造方法中,加入super(3);
在A中加入一個不帶引數的構造方法,A(){}
去掉A中全部的構造方法,則編譯器會自動加入一個不帶引數的構造方法,稱為預設的構造方法
3.2 建立物件時初始化
p = new Person(){{ age=18; name=“李明”; }};
這樣就 不 用 寫 p. name,p.age,方便一點
這樣可以針對沒有相應建構函式,但又要賦值
注意雙括號
3.3 例項初始化與靜態初始化
例項初始化(Instance Initializers)
在類中直接寫
{ 語句…. }
例項初始化,先於構造方法{}中的語句執行(先於this或super之外的那些語句)
儘量少用這種{},有點怪怪的感覺。。
靜態初始化(Static Initializers)
static { 語句…. } 只是與類有關的而不是跟例項有關的
靜態初始化,在第一次使用這個類時要執行,
但其執行的具體時機是不確定的
但是可以肯定的是:總是先於例項的初始化
例子:
class InitialTest
{
public static void main(String[] args)
{
new InitialTest2(6);
}
int n=10; //step2
{
n++;
System.out.println("InitialTest..."+n);
}
static int x;
static
{
x++;
System.out.println("static..." +x);
}
}
class examp extends InitialTest{
examp(int a){
this.a=a;
System.out.println("this.a=" + a );
}
int a;//例項初始化的語句
{
System.out.println("InitialTest2..."+this.a);
}
static//這個初始化要先於例項的初始化
{
x++;
System.out.println("static2..." +x);
}
}
//輸出:
//static...1
//static...1
//static2...2
//InitialTest...11
//InitialTest2...0
//this.a=6
3.4 構造方法的執行過程
構造方法的執行過程遵照以下步驟:
呼叫本類或父類的構造方法,直至最高一層(Object)
按照宣告順序執行欄位的初始化賦值
執行建構函式中的其它各語句(不包括this或super)
簡單地說:
先父類構造,再本類成員賦值,最後執行構造方法中的語句。
例子:
class JavaPConstructor
{
int a=2000;
JavaPConstructor(){
this.a=3000;
}
}
下面我們看一下反彙編後的結果
Compiled from "JavaPConstructor.java"
class text3.JavaPConstructor {
int a;
text3.JavaPConstructor();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: aload_0
5: sipush 2000
8: putfield #12 // Field a:I
11: aload_0
12: sipush 3000
15: putfield #12 // Field a:I
18: return
}
可見,它先呼叫了object方法,雖然沒有寫super,但實際上他會呼叫super。第二步把兩千賦值到欄位裡面。(執行例項初始化及對欄位的賦值)第三步才執行構造方法裡面的語句
例子:(這次有super有this)
class ConstructS
{
public static void main(String[] args){
Person p = new Student("李明", 18, "北大");
}
}
class Person
{
String name="未命名"; //step 2
int age=-1;
Person( String name, int age ){
super(); //step 1
//step 3
System.out.println( "開始構造Person(),此時this.name="+this.name+",this.age="+this.age );
this.name=name; this.age=age;
System.out.println( "Person()構造完成,此時this.name="+this.name+",this.age="+this.age );
}
}
class Student extends Person
{
String school="未定學校"; //step2
Student( String name, int age, String school ){
super( name, age ); //step 1看似一步其實包含父類三步
//step 3
System.out.println( "開始構造Student(),此時this.name="+this.name+",this.age="+this.age+",this.school="+this.school );
this.school = school;
System.out.println( "Student()構造完成,此時this.name="+this.name+",this.age="+this.age+",this.school="+this.school );
}
}
//輸出結果:
//開始構造Person(),此時this.name=未命名,this.age=-1
//Person()構造完成,此時this.name=李明,this.age=18
//開始構造Student(),此時this.name=李明,this.age=18,this.school=未定學校
//Student()構造完成,此時this.name=李明,this.age=18,this.school=北大
構造方法內部呼叫別的的方法
如果這個方法是虛方法,結果如何?
從語法上來說這是合法的,但有時會造成事實上的不合
class ConstructorInvokeVirtual
{
public static void main(String[] args){
Person p = new Student("Li Ming", 18, "PKU");
}
}
class Person
{
String name="未命名";
int age=-1;
Person( String name, int age ){
this.name=name; this.age=age;
sayHello();
}
void sayHello(){
System.out.println( "A Person, name: " + name + ", age: "+ age );
}
}
class Student extends Person
{
String school="未定學校";
Student( String name, int age, String school ){
super( name, age );
this.school = school;//賦值這是第三步才執行的
}
void sayHello(){//子類覆蓋了父類的sayhello,父類就會一下跳到子類,但是此時還沒有賦值好
System.out.println( "A Student, name:" + name + ", age: "+ age + ", school: " + school );
}
}
在本例中,在構造方法中呼叫了一個動態繫結的方法sayHello(),這時, 會使用那個方法被覆蓋的定義,而這時物件嘗未完全構建好,所以 School還沒有賦值。
在構造方法中儘量避免呼叫任何方法,儘可能簡單地使物件進入就緒 狀態
惟一能夠安全呼叫的是final的方法。這就不會有虛方法的問題
4 . 物件清除與垃圾回收
我們知道:new建立物件 那麼如何銷燬物件?
Java中是自動清除 不需要使用delete
4.1 物件的自動清除
垃圾回收(garbage collection )
物件回收是由 Java虛擬機器的垃圾回收執行緒來完成的。
為什麼系統知道物件是否為垃圾
任何物件都有一個引用計數器,當其值為0時,說明該物件可以回收
(任何物件我們要用它,它都是一個引用)(如果物件實體空間沒有被任何引用所引用,那麼其值為零)
引用計數示意(可見他是自動的)
String method(){
String a,b;
a=new String(“hello world”);
b=new String(“game over”);
System.out.println(a+b+”Ok”);
a=null; //hello world 沒被引用
a=b; //game over有兩個引用
return a;
}//結束了,引用都沒了,但是如果呼叫了這個函式,又有引用了
System.gc ()方法
System.gc()方法
它是System類的static方法
它可以要求系統進行垃圾回收
但它僅僅只是”建議(suggest)”
你沒法強制,只是希望虛擬機器有空有條件時候進行垃圾回收
finalize() 方法
Java中沒有“析構方法(destructor)”
但Object的finalize() 有類似功能
系統在回收時會自動呼叫物件的finalize() 方法。
protected void finalize() throws Throwable{}
子類的finalize()方法
可以在子類的finalize()方法釋放系統資源
**一般來說,子類的finalize()方法中應該呼叫父類的finalize()方法,**以保證父 類的清理工作能夠正常進行。
try -with-resources
由於finalize()方法的呼叫時機並不確定,所以一般不用finalize()
關閉開啟的檔案、清除一些非記憶體資源等工作需要進行處理
可以使用try-with-resources語句(JDK1.7以上)
對於實現了java.lang.AutoCloseable的物件
try( Scanner scanner= new Scanner( … ) ){
。。。。。。
}
會自動呼叫其close()方法,相當於,不管你try裡面正常異常都會做下面的事情
finally{
Scanner.close();
}
5 . 內部類與匿名類
定義
內部類( inner class )是在其他類中的類 (其他的類中再定義類)
匿名類( anonymous class)是一種特殊的內部類,它沒有類名。
內部類(Inner class)
內部類的定義
將類的定義class xxxx{…}置入一個類的內部即可
編譯器生成xxxx$xxxx這樣的class檔案
內部類不能夠與外部類同名
內部類的使用
在封裝它的類的內部使用內部類,與普通類的使用方式相同
在其他地方使用
類名前要冠以外部類的名字。
在用new建立內部類例項時,也要在 new前面冠以物件變數。
外部物件名.new 內部類名(引數)
例子:
class TestInnerClass{
public static void main( String[] args ){
Parcel p = new Parcel();
p.testShip();
Parcel.Contents c = p.new Contents(33);//前面要加上外部類的類名
Parcel.Destination d = p.new Destination( "Hawii" );
p.setProperty( c, d );
p.ship();
}
}
class Parcel {
private Contents c;
private Destination d;
class Contents {
private int i;
Contents( int i ){ this.i = i; }
int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {label = whereTo;}
String readLabel() { return label; }
}
void setProperty( Contents c, Destination d ){
this.c =c; this.d = d;
}
void ship(){
System.out.println( "move "+ c.value() +" to "+ d.readLabel() );
}
public void testShip() {
c = new Contents(22);
d = new Destination("Beijing");
ship();
}
}
在內部類中使用外部類的成員
內部類中可以直接訪問外部類的欄位及方法
即使private也可以
如果內部類中有與外部類同名的欄位或方法,則可以用
外部類名.this.欄位及方法
(平時用this都是指當前的,但是你這個指的是外部的欄位及方法)
例子:
public class TestInnerThis
{
public static void main(String args[]){
A a = new A();
A.B b = a.new B();
b.mb(333);
}
}
class A
{
private int s = 111;
public class B {
private int s = 222;
public void mb(int s) {
System.out.println(s); // 區域性變數s
System.out.println(this.s); // 內部類物件的屬性s
System.out.println(A.this.s); // 外層類物件屬性s
}
}
}
//333
//222
//111
內部類的修飾符
內部類與類中的欄位、方法一樣是外部類的成員,它的前面也可以有 訪問控制符和其他修飾符。
訪問控制符:public,protected,預設(沒有修飾符)及private。
注:外部類只能夠使用public修飾或者預設 (package)
final,abstract(表示他是不可繼承的)
static 修飾符
用static修飾內部類 表明該內部類實際是一種外部類 (物件.new就不用了)
因為它與外部類的例項無關
有人認為static的類是巢狀類(nested class),不是內部類inner class
static類在使用時:
1、例項化static類時,在 new前面不需要用物件例項變數; (因為他和例項無關)
2、static類中不能訪問其外部類的非static的欄位及方法,既只能夠訪問static成員。
3、static方法中不能訪問非static的域及方法,也不能夠不帶字首地new 一個非 static的內部類。
例子:
class TestInnerStatic
{
public static void main(String[] args)
{
A.B a_b = new A().new B(); // ok
A a = new A();
A.B ab = a.new B();
Outer.Inner oi = new Outer.Inner();
//Outer.Inner oi2 = Outer.new Inner(); //!!!error
//Outer.Inner oi3 = new Outer().new Inner(); //!!! error
}
}
class A
{
private int x;
void m(){
new B();
}
static void sm(){
//new B(); // error!!!!
}
class B
{
B(){ x=5; }
}
}
class Outer
{
static class Inner
{
}
}
區域性類
在一個方法中也可以定義類,這種類稱為”方法中的內部類”
或者叫區域性類(local class)
例子:
class TestInnerInMethod
{
public static void main(String[] args)
{
Object obj = new Outer().makeTheInner(47);
System.out.println("Hello World!" + obj.toString() );
}
}
class Outer
{
private int size = 5;
public Object makeTheInner( int localVar )//普通的方法
{
final int finalLocalVar = 99;
class Inner {//這就是區域性類
public String toString() {
return ( " InnerSize: " + size + //可以訪問外部類的成員
// " localVar: " + localVar +
// Error! 不能訪問內部普通的變數
" finalLocalVar: " + finalLocalVar
//可以訪問該方法的final區域性變數
);
}
}
return new Inner();
}
}
//Hello World! InnerSize: 5 finalLocalVar: 99
使用區域性類
1、同區域性變數一樣,方法中的內部類
不能夠用 public,private,protected,static修飾,(不能修飾區域性變數同類也不能修飾區域性類) 但可以被final(不可繼承)或者abstract(抽象)修飾。
2、可以訪問其外部類的成員
3、不能夠訪問該方法的區域性變數,(它是隨時產生隨時消失的,進到方法裡就有,退出就消失了,是不可捉摸的不可訪問的)除非是final區域性變數
匿名類
匿名類( anonymous class)是一種特殊的內部類
它沒有類名,在定義類的同時就生成該物件的一個例項
“一次性使用”的類(所以沒必要給他個名字)
(一般他是擴充套件一個或者說要繼承一個類,實現一個介面)
例子:
class TestInnerAnonymous
{
public static void main(String[] args)
{
Object obj = new Outer().makeTheInner(47);
System.out.println("Hello World!" + obj.toString() );
}
}
class Outer
{
private int size = 5;
public Object makeTheInner( int localVar )
{
final int finalLocalVar = 99;
return new Object() {
//這個類沒取名字,但是總得要寫,所以他寫它父類的名字
//或者它實現介面的名字
public String toString() {
return ( " InnerSize: " + size +
" finalLocalVar: " + finalLocalVar
);
}
};//實際上是方法體裡面的類的簡寫,把new這個物件以及定義這個物件一起寫
}
}
//Hello World! InnerSize: 5 finalLocalVar: 99
匿名 類的使用
1、不取名字,直接用其父類或介面的名字。
也就是說,該類是父類的子類,或者實現了一個介面
編譯器生成 xxxxx$1之類的名字
2、類的定義的同時就建立例項,即類的定義前面有一個new
new 類名或介面名(){……}
不使用關鍵詞class,也不使用extends及implements。直接寫父類的名字就完事
3、在構造物件時使用父類構造方法
不能夠定義構造方法,因為它沒有名字
如果new物件時,要帶引數,則使用父類的構造方法
匿名 類的應用
用到介面的事件處理 (繼承一個類)
註冊一個事件偵聽器
示例 AutoScore.java 中
//SymAction lSymAction = new SymAction();
//btnNew.addActionListener(lSymAction);
btnNew.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event)
{
btnNew_ActionPerformed(event);
}
});
作為方法的引數
排序,給一個比較大小的介面
如 SortTest.java
Arrays.<Book>sort( books, new Comparator<Book>(){
//需要一個介面去比較這兩本書
public int compare(Book b1, Book b2){
return b1.getPrice()-b2.getPrice();
}
});
6 . Lambda表示式
Lambda表示式是從Java8增加的新語法
Lambda表示式(λ expression)的基本寫法
(引數)->結果
如 (String s) -> s.length()
如 x->x*x
如 () -> { System.out.println(“aaa”); }
大體上相當於其他語言的“匿名函式”或“函式指標”
在Java中它實際上是“ 匿名類的一個例項
例子:
class LambdaRunnable {
public static void main(String argv[]) {
Runnable doIt = new Runnable(){
public void run(){
System.out.println("aaa");
}
};
new Thread( doIt ).start();
Runnable doIt2 = ()->System.out.println("bbb");//簡潔
new Thread( doIt2 ).start();
new Thread( ()->System.out.println("ccc") ).start();
//作為執行緒的一個引數更簡潔
}
}
可以看出
Lambda表示式是介面或者說是介面函式的簡寫
其基本寫法是引數->結果
這裡,引數是()或1個引數或 (多個引數)
結果是指 表示式 或 語句 或 {語句}
例子:
@FunctionalInterface
interface Fun { double fun( double x );}
public class LambdaIntegral
{
public static void main(String[] args)
{
double d = Integral( new Fun(){
public double fun(double x){
return Math.sin(x);
}
}, 0, Math.PI, 1e-5 );
//簡寫
d = Integral( x->Math.sin(x),
0, Math.PI, 1e-5 );
System.out.println( d );
d = Integral( x->x*x, 0, 1, 1e-5 );
System.out.println( d );
}
static double Integral(Fun f, double a, double b, double eps)// 積分計算
{
int n,k;
double fa,fb,h,t1,p,s,x,t=0;
fa=f.fun(a);
fb=f.fun(b);
n=1;
h=b-a;
t1=h*(fa+fb)/2.0;
p=Double.MAX_VALUE;
while (p>=eps)
{
s=0.0;
for (k=0;k<=n-1;k++)
{
x=a+(k+0.5)*h;
s=s+f.fun(x);
}
t=(t1+h*s)/2.0;
p=Math.abs(t1-t);
t1=t;
n=n+n;
h=h/2.0;
}
return t;
}
}
Lambda大大地簡化了書寫
線上程的例子中
new Thread( ()->{ … } ).start();
在積分的例子中
d = Integral( x->Math.sin(x), 0, 1, EPS );
d = Integral( x->x*x, 0, 1, EPS );
d = Integral( x->1, 0, 1, EPS );
在按鈕事件處理中
btn.addActionListener( e->{ … } ) );
能寫成 Lambda的介面的條件
由於Lambda只能表示一個函式,所以
能寫成Lambda的介面要求包含且最多隻能有一個抽象函式
這樣的介面可以(但不強求)用註記
@FunctionalInterface 來表示。稱為函式式介面
如
@FunctionalInterface
interface Fun { double fun( double x );}
再舉一例:排序
Comparator<Person> compareAge = (p1, p2) -> p1.age-p2.age;
Arrays.sort(people, compareAge);
Arrays.sort(people, (p1, p2) -> p1.age-p2.age);
Arrays.sort(people, (p1, p2) -> (int)(p1.score-p2.score));
Arrays.sort(people, (p1, p2) -> p1.name.compareTo(p2.name));
Arrays.sort(people, (p1, p2) -> -p1.name.compareTo(p2.name));
Lambda表示式,不僅僅是簡寫了程式碼,
更重要的是:
它將程式碼也當成資料來處理
函數語言程式設計
7 . 裝箱、列舉、註解
基本型別的包裝類
基本型別的包裝類
它將基本型別(primitive type) 包裝成Object(引用型別)(因為基本型別沒法當物件用)
如int->Interger
共8類:
Boolean, Byte, Short, Character, Integer, Long, Float, Double
Integer I = new Integer(10);
裝箱與拆箱
裝箱(Boxing) Integer I = 10;
拆箱(Unboxing) int i = I;
實際譯為
Integer I= Integer.valueOf(10);
int i = I.intValue();
主要方便用於集合中,如: Object [] ary = { 1, “aaa”};
列舉
列舉(enum)是一種特殊的class型別
在簡單的情況下,用法與其他語言的enum相似
enum Light { Red, Yellow, Green };
Light light = Light.Red;
但實際上,它生成了 class Light extends java.lang.Enum
自定義列舉
可以在enum定義體中,新增欄位、方法、構造方法
enum Direction
{
EAST("東",1), SOUTH("南",2),
WEST("西",3), NORTH("北",4);
private Direction(String desc, int num){
this.desc=desc; this.num=num;
}
private String desc;
private int num;
public String getDesc(){ return desc; }
public int getNum(){ return num; }
}
註解
註解(annotation)
又稱為註記、標記、標註、註釋(不同於comments)
是在各種語法要素上加上附加資訊,以供編譯器或其他程式使用
所有的註解都是 java.lang.annotation. Annotation 的子類
常用的註解,如
@Override 表示覆蓋父類的方法
@Deprecated 表示過時的方法
@SuppressWarnings 表示讓編譯器不產生警告
自定義註解,比較複雜
public @interface Author {
String name();
}
8 . 沒有指標的Java語言
引用(reference)實質就是指標(pointer)
但是它是受控的、安全的
比如
會檢查空指引
沒有指標運算 *(p+5)
不能訪問沒有引用到的記憶體
自動回收垃圾
C 語言指標在Java中的體現
(1)傳地址 ->物件
引用型別,引用本身就相當於指標
可以用來修改物件的屬性、呼叫物件的方法
基本型別:沒用對應的
如交換兩個整數
void swap(int x, int y){ int t=x; x=y; y=t; }
int a=8, b=9; swap(a.b);
一種變通的辦法,傳出一個有兩個分量x,y的物件
(2)指標運算 -> 陣列
*(p+5) 則可以用 args[5]
(3)函式指標 -> 介面、Lambda表示式
例:求積分,執行緒 、回撥函式、事件處理
(4)指向結點的指標-> 物件的引用
class Node {
Object data;
Node next;
}
(5)使用JNI
Java Native Interface(JNI)
它允許Java程式碼和其他語言寫的程式碼進行互動
java中相等還是不等
基本 型別的相等
數值型別:轉換後比較
浮點數,最好不直接用==
Double.NAN==Double.NAN 結果為false
請參見JDK的API文件
boolean型無法與int相比較
裝箱 物件是否相等
Integer i = new Integer(10);
Integer j = new Integer(10);
System.out.println(i==j); //false,因為物件是兩個
Integer m = 10;
Integer n = 10;
System.out.println(m==n); //true,因為物件有快取
Integer p = 200;
Integer q = 200;
System.out.println(p==q); //false,因為物件是兩個,不能超過-128到+127
注意快取
If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f, or an int or short number between -128 and 127 (inclusive), then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.
列舉、引用物件是否相等
列舉型別
內部進行了惟一例項化,所以可以直接判斷
引用物件
是直接看兩個引用是否一樣
如果要判斷內容是否一樣,則要重寫equals方法
如果重寫equals方法,則最好重寫 hashCode()方法
String 物件的特殊性
String物件
判斷相等,一定不要用==,要用equals
但是字串常量( String literal)及字串常量會進行內部化(interned), 相同的字串常量是 = =的
例子:
String hello = "Hello", lo = "lo";
System.out.println( hello == "Hello"); //true
System.out.println( Other.hello == hello ); //true
System.out.println( hello == ("Hel"+"lo") ); //true
System.out.println( hello == ("Hel"+lo) ); //false
System.out.println( hello == new String("Hello")); //false
System.out.println( hello == ("Hel"+lo).intern()); //true