文章目录
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