Java学习笔记Day04 深入理解Java语言

第四章 深入理解Java语言

4.1 变量及其传递

基本类型变量与引用型变量

  • 基本类型(primitive type):其值直接存于变量中。
  • 引用型(reference type)的变量除占据一定的内存空间外,它所引用的对象实体(由new创建)也要占据一定空间

引用型变量与对象实体的关系

MyDate m,n;
m = new MyDate();
n = m;
n.addYear();

字段变量与局部变量

字段变量(field)与局部变量(Local variable)

  • 前者是在类中,后者是方法中定义的变量或方法的参变量

从内存角度看

  • 存储位置:字段变量为对象的一部分,存在于堆中,局部变量是存在于栈中。
  • 生命周期不同
  • 初始值:字段变量可以自动赋初值,局部变量则须显式赋值。

从语法角度看

  • 字段变量属于类,可以用public,private,static,final修饰
  • 局部变量不能够被访问控制符及static修饰
  • 都可以被final修饰

变量的传递

调用对象方法时,要传递参数。在传递参数时,Java是值传递,即是将表达式的值赋值给形式参数。

对于引用型变量,传递的值是引用值,而不是复制对象实体(可以改变对象的属性)

变量的返回

方法的返回

  • 返回基本类型
  • 返回引用类型,它就可以存取对象实体

不定长参数:用省略号标识,并且是最后一个参数(实际上java当成一个数组)

int sum(int…nums){
 int s = 0;
 for(int n : nums) s+=n;
 return s;
}

调用:sum(1,2,3,4);

4.2 多态和虚方法调用

多态

多态(Polymorphism)是指一个程序中相同的名字表示不同的含义的情况

  • 编译时多态
    • 重载(Overload)(多个同名的不同方法)
  • 运行时多态
    • 覆盖(Override)(子类对父类方法进行覆盖)
    • 动态绑定(dynamic binding)——虚方法调用(virtual method invoking)
    • 在调用方法时,程序会正确的调用子类的方法

多态的特点大大提高了程序的抽象程度和简洁性

上溯造型(upcasting):把派生类型当作基本类型处理

Person p = new Student();
void fun(Person p){}
fun(new Person());

虚方法调用

用虚方法调用,可以实现运行时的多态

  • 子类重载父类方法时,运行时
  • 运行时系统根据调用该方法的实例的类型来决定选择哪个方法调用
  • 所有的final方法都会自动进入动态绑定
void doStuff(Shape s){
    s.draw();
}

Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuff(c);
doStuff(t);
doStuff(l);
动态类型确定

变量 instanceof类型,结果时boolean

什么情况不是虚方法调用

Java中,普通的方法时虚方法

static,private方法不是虚方法调用

static,private与虚方法编译后使用的指令是不同的

三种非虚的方法

static的方法,以声明的类型为准,与实例类型无关

private方法子类看不见,也不会被虚化

final方法子类不能覆盖,不存在虚化问题

4.3 对象构造与初始化

构造方法(constructor)

对象都有构造方法,如果没有,编译器加一个default构造方法

调用本类或父类的构造方法
  • this调用本类的其他构造方法
  • super调用直接父类的构造方法
  • thissuper要放在第一条语句,且只能够有一条

如果没有thissuper,则编译器自动加上super(),即调用直接父类不带参数的构造方法,因为必须令所有父类的构造方法都得到调用,否则整个对象的构建就可能不正确

创建对象时初始化

p = new Person(){{age=18;name ="aaa";}}

可以正对没有响应构造函数,但又要赋值(注意双括号

实例初始化与静态初始化

实例初始化(Instance Initializers)

  • 在类中直接写{语句},先于构造方法{}中的语句执行

静态初始化(Static Initializers)

  • static{语句}在第一次使用这个类时要执行,但其执行的具体时机是不确定的,但总是先于实例的初始化

构造方法的执行过程

构造方法的执行过程遵照以下步骤

  • 调用本类或父类的构造方法,直至最高一层(Object
  • 按照声明顺序执行字段的初始化赋值
  • 执行构造函数中的各语句

即先父类构造,再本类成员赋值,最后执行构造方法中的语句

在构造方法中尽量避免调用任何方法,尽可能简单地使对象进入就绪状态

唯一能够安全调用的是final的方法

4.4 对象清除与垃圾回收

对象的自动清除

垃圾回收(garbage collection)是由Java虚拟机的垃圾回收线程来完成的

任何对象都有一个引用计数器,当其值为0时,说明该对象可以回收,即系统知道该对象为垃圾

String method(){
    String a,b;
    a = new String("hello world");
    b = new String("game over";);
    Systems.out.println(a+b+"OK");
    a = null;
    a = b;
    return a;
}

System.gc()方法,是System类的static方法,它可以要求系统进行垃圾回收,但仅仅只是建议

finalize()方法

Java中没有“析构方法(destructor)”,但Objectfinalize()有类似功能,系统在回收时会自动调用对象的finalize()方法。

子类的finalize()方法可以释放系统资源,但一般来说,子类的finalize()方法中应该调用父类的finalize()方法,以保证父类的清理工作能够正常进行。

try-with-resources

由于finalize()方法的调用并不确定,所以一般不用finalize(),关闭打开的文件、清除一些非内存资源等工作需要进行处理,可以使用try-with-resources语句

而对于实现了java.lang.AutoCloseable的对象,会自动调用其close()方法。

Scanner scanner = new Scanner();
Scanner.close()

4.5 内部类与匿名类

内部类(inner class)是在其他类中的类

匿名类(anonymous class)是一种特殊的内部类,它没有类名

内部类(Inner class)

内部类定义:

  • 将类的定义置入一个类的内部即可,编译器生成xxxx$xxxx这样的class文件
  • 内部类不能够和外部类同名

内部类的使用

  • 在封装他的类的内部使用内部类,与普通类的使用方式相同
  • 在其他地方使用
    • 类名要冠以外部类的名字
    • 在使用new创建内部类实例时,也要new前面冠以对象变量。即外部对象名.new 内部类名(参数)

在内部类中使用外部类的成员

  • 内部类中可以直接访问外部类的字段及方法,即使private也可以

  • 如果内部类中有与外部类同名的字段或方法,则可以用外部类名.this.字段及方法

内部类的修饰符

内部类与类中的字段、方法一样是外部类的成员,他的前面也可以有访问控制符和其他修饰符。

局部类

在一个方法中也可以定义类,这种类称为”方法中的内部类“或者交局部类(local class)

使用局部类

  • 同局部变量一样,方法中的内部类不能用public,private,protect,static修饰,但可以被final或者abstract修饰

  • 可以访问其外部类的成员

  • 不能狗访问该方法的局部变量,除非是final局部变量

匿名类(annoymous class)

匿名类是一种特殊的内部类,它没有类名,在定义类的同时就生成该对象的一个实例,是”一次性使用”的类

匿名类的使用

  • 不取名字,直接用其父类或接口的名字,即该类是父类的子类,或者实现了一个接口
  • 类的定义的同时就创建实例,即类的定义前面有一个newnew 类名或接口名(){}
  • 在构造对象时使用父类构造方法,不能定义构造方法,因为它没有名字,如果new对象时,要带参数,则使用父类的构造方法

匿名类的应用

  • 界面的事件处理

  • 作为方法的参数

    Arrays.<Book>sort(books,new Comparator<Book>(){
        public int compare(Book b1,Book b2){
            return b1.getPrice()-b2.getPrice();
        }
    });
    

4.6 Lambda表达式

基本写法:

  • (参数)->结果
  • (String s)->s.length()
  • x->x*x
  • ()->{String.out.println("aaa");}

大体相当于其他语言的“匿名函数”和“函数指针”,实际上是“匿名类的一个实例

//普通形式
Runnable doIt = new Runnable(){
    public void run(){
        System.out.println("aaa");
    }
};
new Thread(doIt).start();

//Lambda表达式
Runnable doIt = ()->System.out.println("aaa");
new Thread(deIt).start();

new Thread(()->System.out.println("aaa")).start();

Lambda表达式时接口或者说是接口函数简写

其基本写法是参数->结果,

  • 这里参数是()或1个参数或多个参数

  • 结果是指表达式或语句或{语句}

inteface Fun{double fun(double x);}

double d = Integral(new Fun(){
    public double fun(double x) {
        return Math.sin(x);
    }
},0,Math.PI,1e-5);

double d = Integral(x->Math.sin(x)),0,Math.PI,1E-5);

能写成Lambda的接口的条件

由于Lambda只能表示一个函数,所以能写成Lambda的接口要求包含且最多只能有一个抽象函数

这样的接口可以(不强求)用注记

  • @FunctionalInterface来表示,称为函数式接口
Comparator<Person> compareAge = 
    (p1,p2) -> p1.age - p2.age;
Arrays.sort(people,compareAge);

Array.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表达式,不仅仅是简写了代码,更重要的是他将代码也当成数据来处理

4.7 装箱、枚举、注解

基本类型的包装类

他将基本类型(primitive type)包装成Object(引用类型

eg:int->Integer

共8类:Boolean,Byte,Short,Character,Integer,Long,Float,Double

装箱与拆箱

装箱(Boxing)

拆箱(Unboxing)

4.8 没有指针的Java语言

引用与指针

引用(reference)实质就是指针(pointer),但是它是受控的,安全的,比如

  • 会检查空指引
  • 没有指针运算
  • 不能访问没有引用到的内存
  • 自动回收垃圾

C语言指针在Java中的体现

传地址->对象

引用类型,引用本身就相当于指针,可以用来修改对象的属性,调用对象的方法

基本类型:没用对应的

  • 如交换两个整数

    void swap(int x,int y){
        int t = x;
        x = y;
        y = t;
    }
    
    int a = 8,b=9;
    swap(a,b);
    
指针运算 -> 数组

*(p+5)则可以用arg[5]

函数指针 -> 接口、Lambda表达式
指向结点的指针 -> 对象的引用
class Node{
    Object data;
    Node next;
}
使用JNI

Java Native Interface(JNI)

它允许Java代码与其他语言写的代码进行交互

相等还是不等

==

简单来说,基本类型是值相等,引用类型是引用相等

基本类型的相等

数值类型:转换后比较

浮点数,最好不直接用==

Double.NAN==Double.NAN结果为false

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,因为对象是两个
装箱对象是否相等

注意缓存

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.

枚举、引用对象是否相等

枚举类型

  • 内部进行了唯一实例化,所以可以直接判断

引用对象

  • 是直接看两个引用是否一样
  • 如果要判断内容是否一样,则要重写equal方法
  • 如果重写equal方法,则最好重写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

练习

附件是一个模拟事件消息订阅的程序,请阅读并修改。

主要是加上一个自已的订阅者,并且该订阅者要用匿名类来实现。(基本要求占8分)

如果可以,也加上用Lambda来实现的订阅者。(较高要求,占2分)。

/**
 * 一个简单的新闻事件侦听演示程序
 *
 * @author tds
 * @author 修改者:zhangwentao
 */

public class NewsDemo {
    public static void main(String[] args) {

        //模拟有一家新闻社
        NewsAgency bbc = new NewsAgency("BBC");
        //订阅该社的新闻
        bbc.addListener(new MyListener());

        //TODO 请在这里再加入一个Listener
        //普通形式
        /*bbc.addListener(new Listener() {
            @Override
            public void newsArrived(NewsEvent e){
                if(e.level > 5) {
                    String name = "tao";
                    System.out.println(name +" please note," + e.text + " happed!");
                }
            }
        });*/

        //lambda形式
        bbc.addListener(e -> {
            if (e.level > 5) {
                String name = "tao";
                System.out.println(name + " please note," + e.text + " happed!");
            }
        });


        //新闻社启动其工作流程
        bbc.start();
    }
}


/**
 * 事件信息
 */
class NewsEvent {
    Object source;  //事件来源
    String text;    //事件内容
    int level;      //事件级别

    NewsEvent(Object source, String text, int level) {
        this.source = source;
        this.text = text;
        this.level = level;
    }
}

interface Listener {
    void newsArrived(NewsEvent e);
}

/**
 * 新闻机构
 */
class NewsAgency {
    String name; //机构名

    public NewsAgency(String name) {
        this.name = name;
    }

    Listener[] listeners = new Listener[100]; //侦听者(订阅者)
    int listenerCnt = 0; //已有的订阅者

    //加入一个订阅者
    void addListener(Listener oneListener) {
        if (listenerCnt < listeners.length) {
            listeners[listenerCnt] = oneListener;
            listenerCnt++;
        }
    }

    //模拟一个事件发生,并通知所有的订阅者
    void start() {
        NewsEvent event = new NewsEvent("Mr. Joan", "Bombing", 9);

        for (int i = 0; i < listenerCnt; i++) {
            listeners[i].newsArrived(event);
        }

    }
}

/**
 * 实现一个订阅者
 */
class MyListener implements Listener {
    //当接收到消息后,进行一些显示
    @Override
    public void newsArrived(NewsEvent e) {
        if (e.level > 5) {
            System.out.println("warning :");
        }
        System.out.println("please note," + e.text + " happed!");
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值