栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JDK1.8源码阅读笔记——Object根父类(二)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JDK1.8源码阅读笔记——Object根父类(二)

Object根父类
  • 方法五:object clone()
  • 方法六:String toString()
  • 方法七-十一:notify系列和wait系列
  • 方法十二: void finalize()
  • 相关源码详细注释:

方法五:object clone()
protected native Object clone() throws CloneNotSupportedException;

作用:创建并返回此对象的副本
tips:
1、保证克隆对象将有单独的内存地址分配,即相对原始对象独立存在。
2、原始和克隆的对象应该具有相同的类类型,但并不是强制的。
3、原始和克隆的对象应该是平等的equals()方式使用,但也不是强制的。
4、每个类直接或间接的父类都是Object,所以他们都包含clone方法,但是此方法使用protected修饰的,所以不能再类外访问。如果需要对一个对象进行赋值,就需要对clone方法覆盖
Q:为什么要克隆?
经过clone()方法克隆的对象可能包含一些已经修改过的属性,而新new出来的对象的属性还是初始化时候的值。所以当需要一个新的对象来保存当前对象的状态就是用clone方法。因为clone使用native修饰的,因此它是可以在底层实现的,速度很快!
Besides

Object obj1 = new Objecet();
Object obj2;
obj2 = obj1;

这种形式的代码复制是引用,即对象在内存中的地址,两个对象仍然指向的同一个对象。一损俱损一荣俱荣!

Object obj1 = new Object();
obj1.setName("a");
Object obj2 = (Object)obj1.clone();

clone方法复制的对象和原来的对象是同时独立存在的,互不影响!
So——>
Q:How to achieve the method clone?
在Java语言中,数据类型分为值类型和引用类型。
值类型:int、double、byte、boolean、char等简单数据类型。
引用类型:类、数组、接口等复杂类型。
浅克隆和深克隆的主要区别在于是否支持引用类型成员变量的赋值:
1、被赋值的类需要实现Cloneable接口(如果没有实现会抛出异常)
2、覆盖clone()方法,访问修饰符设置为public。方法中调用super.clone()方法得到需要的复制对象(native为本地底层方法)
在网上找了一个例子:

class Address implements Cloneable {  
      private String add;  
   
      public String getAdd() {  
          return add;  
      }  
    
     public void setAdd(String add) {  
              this.add = add;  
     }  
            //覆盖clone方法
     @Override  
     public Object clone() {  
         Address addr = null;  
         try{  
             addr = (Address)super.clone();  
         }catch(CloneNotSupportedException e) {  
             e.printStackTrace();  
         }  
         return addr;  
     }  
 }  
   
 class Student implements Cloneable{  //实现Clonealbe接口 
     private int number;  
   
     private Address addr;  
       
     public Address getAddr() { 3
              return addr;  
     }  
   
     public void setAddr(Address addr) {  
         this.addr = addr;  
     }  
   
     public int getNumber() {  
         return number;  
     }  
   
     public void setNumber(int number) {  
         this.number = number;  
     }  
       
     @Override  
     public Object clone() {  
         Student stu = null;  
         try{  
             stu = (Student)super.clone();   //浅复制  
         }catch(CloneNotSupportedException e) {  
             e.printStackTrace();  
         }  
         stu.addr = (Address)addr.clone();   //深度复制  
         return stu;  
     }  
 }  
 public class Test {  
       
     public static void main(String args[]) {  
           
         Address addr = new Address();  
         addr.setAdd("杭州市");  
         Student stu1 = new Student();  
         stu1.setNumber(123);  
         stu1.setAddr(addr);  
           
         Student stu2 = (Student)stu1.clone();  
           
         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
           
         addr.setAdd("西湖区");  
           
         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
     }  
 }  
结果:
学生1:123,地址:杭州市  
学生2:123,地址:杭州市  
学生1:123,地址:西湖区  
学生2:123,地址:杭州市  
  • 在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
  • 在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
方法六:String toString()
public String toString() {
        return getClass().getName() + "@" 
        + Integer.toHexString(hashCode());
    }

作用:返回对象的字符串表示形式,建议所有子类都覆盖toString方法。方便所有类的字符串操作。
返回值:对象的字符串(文本形式),类名+@+此对象哈希吗的无符号十六进制表示
例如,System.out.println(xx)括号里的“xx”如果不是String类型,就会自动调用toString方法。
Tip:
1、undefined和null没有toString()方法
也就是说,码代码的时候你不可以这样写!

undefined.toString();//error
null.toString();//error

2、boolean类型的返回对应的true和false

true.toString();//true
false.toString();//false

3、字符串类型就按原值返回

'1'.toString();//'1'
'aaa'.toString();//‘aaa’

4、数值类型的情况

Number.toString();//"function Number(){native code})"

①正浮点数以及NaN、Infinity
(NaN是Not A Number的缩写,用于处理计算中出现的错误情况。Infinity是指无穷大)

0.55.toSting();//'0.55'
NaN.toString();//'Nan'
Infinity.toString();//'Infinity'

②、负浮点数或带“+”的正浮点数。相当于先运行tostring()方法,再添加正负号,转换为数字

+0.55.toString();//0.55
typeof+0.55.toString();//'number'
-0.55.toString();//-0.55
typeof -0.55.toString();//'number'

③、整数直接+toString()形式会报错,提示无效标记,因为整数后的点会被识别为小数点.所以为了避免这个问题需要加括号

0.toString();//Uncaught SyntaxError:Invalid or unexpected token
(0).toString();//'0'
(-0).toString();//'0'
(+0.5).toString();//'0.5'
(-0.5).toString();//'-0.5'
(NaN).toString();//'NaN'

④数据类型的toString方法可以接受表示转换基数(radix)的可选参数,如果不指定参数,转换轨则默认为十进制,同样也可以加参数转换为其他进制数

var a = 17;
n.toString();//'17'
n.toString(2);//'10001'

⑤对象Object类型及自定义对象类型加括号返回

function Person(){
	this.name = 'test';
}
var per = new Person();
per.toString();//"[Object Object]"

返回到最初的println的例子:

public class Main {

	public static void main(String[] args) {
		String a=new String();
		a="hello world";
		System.out.println(a);//相当于
		System.out.println(a.toString());
 
	}
 
}
方法七-十一:notify系列和wait系列


储备:
所有对象都有一个与之关联的锁与监视器。
what?什么是锁?什么是监视器?
锁:逻辑上锁是对象内存堆中头部的一部分数据。JVM中每个对象都有一个锁(互斥锁),任何程序都可以使用它来协调对对象的多线程访问。如果任何线程想要访问该对象的实例变量,那么线程必须拥有该对象的锁(通过在锁内存区域设置一些标志)。所有其他的线程视图访问该对象的变量必须等到拥有该对象的锁的线程释放锁(改变标记)
一旦线程拥有一个锁,他可以多次请求相同的锁,但是在其他线程能够使用这个对象之前必须释放相同次数的锁。
1)锁可以用来保护代码段,任何时刻只能有一个线程执行被保护的代码
2)锁可以管理视图进入被保护代码的线程
3)锁以拥有一个或多个相关的条件对象
从1.0版本开始,Java中的每一个对象都有一个内部锁。如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。也就说,要调用该方法,线程必须获得内部的对象锁。
我的一些理解:
这里可以联想到操作系统的PV操作互斥访问临界资源的相关知识,有几次P操作,就需要几次V操作来释放临界资源。这里大概是相似的意思,均是互斥访问临界资源。
监视器:是一种同步结构,它允许线程同时互斥(使用锁)和协作,即赋予使用等待集(wait-set)使线程等待某些条件为真的能力。
监视器就像一个包含一个特殊房间的家住无,这个特殊房间就是对象实例,每次只能占用一个线程。通常,这个房间包含一些需要放置并发访问的数据。从一个线程进入这个房间到它离开的时间,它可以独占访问房间中的任何数据。
进入监控的建筑被称为“进图监控监视器”
进入建筑颞部特殊房间叫做“获取监视器”
房间占领被称为“拥有监视器”
离开房间被称为“释放监视器”
退出整个建筑被称为“退出监视器”
访问和退出的全过程:
当一个线程访问受保护的数据(进入特殊的房间)时,它首先在建筑物接收(entry-set)中排队。如果没有其他线程在等待(拥有监视器),线程获取锁并继续执行受保护的代码。当线程完成执行时,它释放锁并退出大楼(退出监视器)。
如果当一个线程到达并且另一个线程已经拥有监视器时,它必须在接收队列中等待(entry-set)。当当前所有者退出监视器时,新到达的线程必须与在入口集中等待的其他线程竞争。只有一个线程能赢得竞争并拥有锁。

Java中的每个对象有一个内部的锁和内部条件。如果一个方法用synchronized关键字声明,那么,它表现的就像一个监视器方法。通过wait/notifyAll/nofify来访问条件变量

1、wait():

使当前线程阻塞,处于等待状态(Wait),并将当前线程置入锁对象的等待对了,直到被通知(notify)或被中断。
前提是必须先获得锁,一般配合synchronized关键字使用。即,一般在synchronized同步代码块里使用wait()、notify()/notifyAll()方法

2、notify():
唤醒处于等待状态的线程。在同步方法或同步代码快汇总使用,如果对个线程在等待,随机挑选一个线程唤醒(由jdk版本决定)。notify方法调用后,当前线程不会立即释放对象锁,要等到当前线程执行完毕后再释放。也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程让其获得锁

3、notifyAll():
唤醒所有处于等待状态的线程
notify方法只唤醒一个等待(对象的)线程并使其开始执行。所以如果有多个线程在等待同一个对象,这个方法只会唤醒其中一个,选择哪个线程取决于操作系统对多线程管理的实现。
notifyAll会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。
notify唤醒沉睡的线程后,线程会接着上次的执行继续往下执行。所以在进行条件判断时候,可以先把 wait 语句忽略不计来进行考虑;显然,要确保程序一定要执行,并且要保证程序直到满足一定的条件再执行,要使用while进行等待,直到满足条件才继续往下执行。

public class K {
    //状态锁
    private Object lock;
    //条件变量
    private int now,need;
    public void produce(int num){
        //同步
        synchronized (lock){
           //当前有的不满足需要,进行等待,直到满足条件
            while(now < need){
                try {
                    //等待阻塞
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我被唤醒了!");
            }
           // 做其他的事情
        }
    }
}

由于上面三种方法都是在同步代码快里执行,所以当前线程一定是已经获取了锁的
当线程执行wait()方法时,会释放当前锁,让出CPU(或其他临界资源),进入wait状态
只有当notify或notifyAll执行时,才会唤醒一个或多个等待状态的线程,然后继续执行。直到执行完同步代码快的代码或者中途遇到wait,再次释放锁,是一个类似递归的过程。

方法十二: void finalize()
protected void finalize() throws Throwable { }

作用:回收特殊渠道申请的内存。Java程序有垃圾回收器,所以一般情况下内存问题不用程序员操心。但有一种JNI(Java Native Interface)调用non-Java程序(C或C++),finalize()的工作就是回收这部分的内存。
即当某个对象变成一个垃圾对象,内存被回收时就会调用系统中定义的finalize方法来完成。
不建议用finalize方法完成“非内存资源”的清理,因为Java的自动垃圾回收器不会失去平衡,作为便利的代价,不得不放弃对系统资源释放的控制,Java Applet不会自动执行类中的finalize()方法,即使使用1.0版本试图强制调用也不能确保将它调用。
因此不建议依靠finalize来执行你的Applet和应用程序的资源清理工作。取而代之,我们应当明确得清除那些资源或使用类似try…finally块的机制来实现。

相关源码详细注释:
  
    protected native Object clone() throws CloneNotSupportedException;

    
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    
    public final native void notify();//notify是“通知”的意思

    
    public final native void notifyAll();

    
    public final native void wait(long timeout) throws InterruptedException;

    
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

    
    public final void wait() throws InterruptedException {
        wait(0);
    }

    
    protected void finalize() throws Throwable { }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/324516.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号