菜鸟笔记 -- Chapter 6.3 对象
6.3 对象
Java是一门面向对象的程序设计语言,对象是由类抽象出来的,所有的问题都是通过对象来处理的,对象可以操作类的属性和方法解决相应的问题,所以了解对象的产生、操作和生存周期对学习Java语言是十分必要的。我们以后在探讨JVM的时候再进行对象生命周期的探讨,这里不做介绍。下面详细介绍对象在Java语言中的使用。
6.3.1 对象的创建
在上面我们介绍过对象是同一类事物抽象出来的一个特例,通过这个特例来处理这类事物出现的问题,在Java语言中通过new操作符来创建对象。在讲解构造方法的时候介绍过每实例化一个对象的时候就会自动调用一次构造方法,实质上这个过程就是创建对象的过程。准确地说,可以在Java语言中使用new操作符调用构造方法创建对象。语法格式如下:
ClassName name = new ClassName(); ClassName name = new ClassName(“构造参数A”,“构造参数B”,.....);
name对象被创建出来时,name对象就是一个对象的引用,这个引用在内存中为对象分配了存储空间,可以在构造方法中初始化成员变量,创建对象时,自动调用构造方法,也就是说Java语言中创建对象和初始化可以捆绑在一起进行.每个对象都是相互独立的,在内存中占据独立的内存地址,并且每个对象都具有自己的生命周期,当一个对象的生命周期结束时,对象变成了垃圾,由Java虚拟机自带的回收机制进行处理,处理后不能再被使用.Java中对象的创建除了new还有很多方法,我会在后边总结完反射作为一个Question补充;这里不多讲解:感兴趣参考下面链接:https://www.cnblogs.com/baizhanshi/p/5896092.html
6.3.2 访问对象的属性和方法
我们的类中定义的成员分为类成员和实例成员,上文讲的static修饰的属于类成员类成员直接通过类名调用,而实例成员则通过对象.成员名字来调用。下面我们来看一段代码,然后分析:
package cn.yourick.object;
import javax.jws.Oneway;
public class ObjectDemo {
String name = "Youric";
static String address = "北京";
public static void main(String[] args) {
ObjectDemo objectDemo1 = new ObjectDemo();
ObjectDemo objectDemo2 = new ObjectDemo();
System.out.println(objectDemo1.name+"--"+objectDemo1.address);
objectDemo1.name = "YouricYou";
objectDemo1.address = "神都";
System.out.println(objectDemo1.name+"--"+objectDemo1.address);
System.out.println(objectDemo2.name+"--"+objectDemo2.address);
}
}

我们发现虽然不同的两个对象调用同一个成员变量,结果却不相同,这是因为实例成员由实例自己携带,它属于实例自身。但类成员就不同了,类成员归属类所有,也就是可以被所有的实例共享,所以一个实例改变了类成员的话,那么所以实例指向的都改变了,通过图分析如下:

如上图所示,实例成员会由实例成员自己携带数据,所以操作也是基于自身进行操作的,不影响类本身和其它实例,而类成员则是基于类进行操作,改变了类本身的数据,自然也影响了其它实例;
6.3.3 对象的引用
我们在代码中创建对象,经过编译后会生成一个符号引用,在类加载时符号引用转换为直接引用,即在内存中分配空间,此时我们真正的表示符实质上是一个指向该内存的引用,所以我们一定要搞明白引用并不是对象,而是指向了对象的内存地址,但我们为了方便理解通常简单的说objectDemo1是ObjectDemo的对象.
如下表达式:A a1 = new A; 它代表A是类,a1是引用,a1不是对象,new A才是对象,a1引用指向new A这个对象。(即new A在内存中有一个内存空间,而a1只是一个指向,指向这个内存地址)。在JAVA里,“=”不能被看成是一个赋值语句,它不是在把一个对象赋给另外一个 对象,它的执行过程实质上是将右边对象的地址传给了左边的引用,使得左边的引用指向了右边的对象。JAVA表面上看起来没有指针,但它的引用其实质就是一 个指针,引用里面存放的并不是对象,而是该对象的地址,使得该引用指向了对象。在JAVA里,“=”语句不应该被翻译成赋值语句,因为它所执行的确实不是 一个赋值的过程,而是一个传地址的过程,被译成赋值语句会造成很多误解,译得不准确。再如:A a2;它代表A是类,a2是引用,a2不是对象,a2所指向的对象为空null;再如:a2 = a1;它代表,a2是引用,a1也是引用,a1所指向的对象的地址传给了a2(传址),使得a2和a1指向了同一对象。综上所述,可以简单的记为,在初始化时,“=”语句左边的是引用,右边new出来的是对象。在后面的左右都是引用(a2 = a1)的“=”语句时,左右的引用同时指向了右边引用所指向的对象。再所谓实例,其实就是对象的同义词。
6.3.4 对象的比较
我们在开发中经常需要对对象进行比较,判断对象是否为同一个对象,判断对象有两种方式,分别为”==”和equals()方法。实质上这两种方式有着本质的区别,下面我们通过代码来演示和分析:
package cn.yourick.object;
public class ObjectEquals {
    public static void main(String[] args) {
        test1();
        System.out.println("---------------------");
        test2();
        System.out.println("---------------------");
        test3();
    }
    //比较一
    public static void test1(){
        String name1 = "Youric";
        String name2 = "Youric";
        String name3 = name1;
        String name4 = "youric";
        System.out.println(name1.equals(name2)+"--"+(name1==name2));
        System.out.println(name1.equals(name3)+"--"+(name1==name3));
        System.out.println(name3.equals(name4)+"--"+(name3==name4));
    }
    //比较二
    public static void test2(){
        String name1 = new String("Youric");
        String name2 = new String("Youric");
        String name3 = name1;
        String name4 = new String("youric");
        System.out.println(name1.equals(name2)+"--"+(name1==name2));
        System.out.println(name1.equals(name3)+"--"+(name1==name3));
        System.out.println(name3.equals(name4)+"--"+(name3==name4));
    }
    //比较三
    public static void test3(){
        ObjectEquals objectEquals1 = new ObjectEquals();
        ObjectEquals objectEquals2 = new ObjectEquals();
        ObjectEquals objectEquals3 = objectEquals1;
        System.out.println(objectEquals1.equals(objectEquals2)+"--"+(objectEquals1==objectEquals2));
        System.out.println(objectEquals1.equals(objectEquals3)+"--"+(objectEquals1==objectEquals3));
    }
}

从上面的结果看,我们可能很莫名其妙,我们首先针对equals()和”==”分别说一下,”==”是一个判断运算符,它要求的是两个比较对象的hashcode返回要求一样,而每个对象都有自己唯一的hashcode返回,所以通常我们也管hashcode返回的作为地址,实际他和地址并不一样,只是因为它唯一所以我们用来形容他和对象地址的关系.下面是Object的hashcode()和搜集的hashcode()底层代码:http://blog.csdn.net/bluetjs/article/details/52610414
public native int hashCode();//可以看出这是一个本地方法,也就是C语言写就的
聊完”==”,聊一下equals(),我们知道Object是所有类的父类,那么也就意味着不管是我们自己写的类,还是JDK提供的类,它们中要么是继承了Object中的方法,要么是重写了Object中的方法.而equals()正是Object中的方法,源码如下:
public boolean equals(Object obj) {
        return (this == obj);
    }
通过这段代码,我们知道如果不重写equals(),那么equals()判断就只能是和”==”的结果一样了,而上面我们看到String的equals(),那么String的equals()又是如何的呢?
private final char value[];
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
从上面我们看到,String完成了对equals()的重写,在equals()中有两个判读,第一个判断和”==”是一样的,如果==那么直接返回true,否则进行下一个判断,判断这个对象是String的对象吗?是的话,那么获取调用该equals()的字符串的长度,然后判断该字符串的长度和要比较的字符串对象的长度,如果相等的话,那么下面就是将字符串转换为char数组,然后挨个比较字符.所以字符串的equals()最低要求是内容相同即可,不要求地址一定相同.
通过上面的讲述,我们就明白上面各种结果的缘由了,下面我们讲几个对象的hashcode打印出来如下:
package cn.yourick.object;
public class ObjectEquals {
    public static void main(String[] args) {
        test1();
        System.out.println("---------------------");
        test2();
        System.out.println("---------------------");
        test3();
    }
    //比较一
    public static void test1(){
        String name1 = "Youric";
        String name2 = "Youric";
        String name3 = name1;
        String name4 = "youric";
        System.out.println(name1.equals(name2)+"--"+(name1==name2));
        System.out.println(name1.equals(name3)+"--"+(name1==name3));
        System.out.println(name3.equals(name4)+"--"+(name3==name4));
        System.out.println("name1:"+name1.hashCode());
        System.out.println("name2:"+name2.hashCode());
        System.out.println("name3:"+name3.hashCode());
        System.out.println("name4:"+name4.hashCode());
    }
    //比较二
    public static void test2(){
        String name1 = new String("Youric");
        String name2 = new String("Youric");
        String name3 = name1;
        String name4 = new String("youric");
        System.out.println(name1.equals(name2)+"--"+(name1==name2));
        System.out.println(name1.equals(name3)+"--"+(name1==name3));
        System.out.println(name3.equals(name4)+"--"+(name3==name4));
        System.out.println("name1:"+name1.hashCode());
        System.out.println("name2:"+name2.hashCode());
        System.out.println("name3:"+name3.hashCode());
        System.out.println("name4:"+name4.hashCode());
    }
    //比较三
    public static void test3(){
        ObjectEquals objectEquals1 = new ObjectEquals();
        ObjectEquals objectEquals2 = new ObjectEquals();
        ObjectEquals objectEquals3 = objectEquals1;
        System.out.println(objectEquals1.equals(objectEquals2)+"--"+(objectEquals1==objectEquals2));
        System.out.println(objectEquals1.equals(objectEquals3)+"--"+(objectEquals1==objectEquals3));
        System.out.println("objectEquals1:"+objectEquals1.hashCode());
        System.out.println("objectEquals2:"+objectEquals2.hashCode());
        System.out.println("objectEquals3:"+objectEquals3.hashCode());
    }
}

关于String的两种创建,我们在常见对象--String中再详细介绍;
6.3.5 对象的另类判断
在上面String类的equals()在我们发现有这么一条语句anObject instanceof String,这条语句是判断anObject 是否是String类的一个实例,instanceof是Java的一个关键字用于判断实例是否属于类;
我们在项目开发中会经常用到一个对象转型的问题,如果父类对象不是子类对象的实例,就会发生ClassCastException异常,所以在执行向下转型之前应该首先进行一下类型判断.下面通过一段代码来验证一下;
package cn.yourick.object;
import javax.jws.Oneway;
import cn.yourick.statickey.Test;
public class ObjectInstanceof extends ObjectDemo{
    public static void main(String[] args) {
        /**
         * 我们如果不判断,那么就会成为运行时异常 java.lang.ClassCastException
         */
        //ObjectInstanceof objectInstanceof = (ObjectInstanceof) new ObjectDemo();
        //通过instanceof关键字有效解决运行时异常
        test();
    }
    //测试一
    public static void test(){
        ObjectInstanceof objectInstanceof = new ObjectInstanceof();
        ObjectDemo objectDemo = new ObjectDemo();
        if(objectDemo instanceof ObjectInstanceof){
            objectInstanceof = (ObjectInstanceof) objectDemo;
            System.out.println("转换成功!");
        }else {
            System.out.println("转换失败!");
        }
    }
}

注意:实例不能是基本数据类型,null用操作符instanceof测试任何类型时都是返回false的;
6.3.6 向下转型和向上转型
在上面我们初次认识了类型转换,类型转换其实和继承时息息相关的,也是多态的一种体现,本人有了一定的Java基础,所以这里先做转型介绍,如果有疑问的可以先看后面的继承再回过头看转型,根据自己,不一而足.下面开始介绍:
对象类型的转换主要分为向上转换和向下转换。向上转型就是将子类对象赋值给父类对象,这是多态思想的体现,通过向上转型我们可以在父类中定义一个方法,它的子类重写方法,通过传递不同的子类可以实现该方法的不同实现。向上转型是由具体的向抽象的转变,所以总是安全的,不会存在问题。
向下转型则和向上转型截然相反,是将父类对象传递给子类对象,也即将将抽象的类转换为具体的类,这样的转换通常会出现问题,下面我们通过代码来体现:
package cn.yourick.object;
public class ObjectChange extends ObjectInstanceof{
    public static void main(String[] args) {
        ObjectChange objectChange = new ObjectChange();
        objectChange.changeUp();//向上转型
        objectChange.changeDown();//向下转型
    }
    //重写父类方法
    @Override
    public void test() {
        System.out.println("重写test()!");
    }
    //向上转型
    public void changeUp(){
        ObjectInstanceof objectInstanceof = new ObjectChange();
        objectInstanceof.test();//父类对象调用子类方法
    }
    //向下转型,通常会出现错误
    public void changeDown(){
        ObjectInstanceof objectInstanceof = new ObjectInstanceof();
        ObjectChange objectChange = new ObjectChange();
        //向下转换,通常先用instanceof判断,避免出现运行时异常
        if(objectInstanceof instanceof ObjectChange){
            objectChange = (ObjectChange) objectInstanceof;
            System.out.println("转型成功!");
        }else{
            System.out.println("转型失败!");
        }
    }
}

越是具体的对象,越是具有较多的特性,而越是抽象的对象具有的特性也就相对较小了。向下转型通常需要将对象进行强制转换,要特别注意,小心出错。
菜鸟笔记 -- Chapter 6.3 对象的更多相关文章
- 菜鸟笔记 -- Chapter 6 面向对象
		在Java语言中经常被提到的两个词汇是类与对象,实质上可以将类看作是对象的载体,它定义了对象所具有的功能.学习Java语言必须要掌握类与对象,这样可以从深层次去理解Java这种面向对象语言的开发理念, ... 
- 菜鸟笔记 -- Chapter 6.4  面向对象的三大特性
		6.4.1 三大特性概述 面向对象的三大特性是Java中一个很重要的基本理念. 封装是面向对象的核心思想.将对象的属性和行为封装起来,其载体就是类,类通常对客户隐藏其实现细节,这就是封装的意思.采用 ... 
- 菜鸟笔记 -- Chapter 6.2 类的构成
		在前面我们讲过高级开发语言大多由7种语法构成,但这是一个很空泛的概述,下,面我们仅就针对Java程序来说一下构成一个Java程序的几大部分,其中类是最小的基本元素.类是封装对象属性和行为的载体,而在J ... 
- 菜鸟笔记 -- Chapter 4 Java语言基础
		在Chapter3中我们写了第一个Java程序Hello World,并且对此程序进行了分析和常见错误解析.那么我们有没有认真观察一下Java程序的基本结构呢?本节我就来聊一下Java程序的基本结构( ... 
- 菜鸟笔记  -- Chapter 1   计算机从0到1
		进入20世纪第二个十年,计算机已经成为生活中一个必不可小的工具了,但我们真的了解计算机吗?计算机有哪些部分构成?不同的计算机又可以做什么样的事情呢?我们的PC和用来做加减乘除的计算器都属于计算机范畴吗 ... 
- 菜鸟笔记  --  Chapter 11  格式化
		我们在String中介绍过它有一个格式化的方法,在其它很多地方,也都能看到格式化的操作,那么这节我们就来认真了解一下Java中的格式化操作. 我们在操作中涉及到的格式化有字符串的格式化和一些其它数据类 ... 
- 菜鸟笔记 -- Chapter 6.4.3  多态
		6.4.3 多态 多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方 ... 
- 菜鸟笔记 -- Chapter 6.4.2  详解继承
		6.4.2 详解继承 6.4.2.1 继承入门 继承使得程序架构具有一定的弹性,在程序中复用一些已经定义完善的类不仅可以减少软件开发周期,也可以提高软件的可维护性和可扩展性.基本思想是基于某个父类 ... 
- 菜鸟笔记 -- Chapter 6.2.5  代码块
		6.2.5 代码块 在编程过程中我们通常会遇到如下这种形式的程序: package democlass; public class CodeBlock { { System.out.println( ... 
随机推荐
- C#请求http向网页发送数据,网页接收
			首先,我们需要的是什么东西? 用POST方式请求http,给网页传输数据,网页接收到数据之后,把数据存储到数据库中. 1.首先请求http,建立连接,把转码过的数据传输过去 2.网页接收数据,在转码之 ... 
- git把dev部分提交过的内容合并到master
			git 把dev部分提交过的内容合并到master $ git reflog a6de5cc HEAD@{}: checkout: moving from wf_dev to master 303aa ... 
- jquery——write less,do more
			rite less, do more.这句话想必是很多语言都提倡的. 在此举三个jquery的应用体现 一.绑定多个事件类型 $("div").bind("mouseov ... 
- OLEDB 参数化查询
			一般情况下,SQL查询是相对固定的,一条语句变化的可能只是条件值,比如之前要求查询二年级学生信息,而后面需要查询三年级的信息,这样的查询一般查询的列不变,后面的条件只有值在变化,针对这种查询可以使用参 ... 
- 正则表达式把所有Paul替换成Ringo:Paul Puala Pualine paul Paul
			代码实现如下: <!DOCTYPE html><html><body> <h2>JavaScript Regular Expressions</h ... 
- html-其他常见标签的使用
			b:加粗 s:删除线 u:下划线 i:斜体 per:原样输出 sup:上标 sub:下标 p:段落标签 比br多一行 (CSS) div:自动换行 span:在一行显示 完整代码: <html& ... 
- Flink -- Java Generics Programming
			Flink uses a lot of generics programming, which is an executor Framework with cluster of executor ha ... 
- alpinelinux
			https://wiki.alpinelinux.org/wiki/Tutorials_and_Howtos https://nixos.org/nix/manual/#ch-installing-b ... 
- 19_AOP概述
			[AOP的使用场景] 性能测试 访问控制 事务管理 日志记录 [AOP相关术语] [ 连接点 Joinpoint ] 程序执行的某个特定位置.(假如Car类有drive()方法,那么在drive()方 ... 
- 《ArcGIS Runtime SDK for Android开发笔记》——数据制作篇:紧凑型切片制作(Server缓存切片)
			1.前言 在ArcGIS 10中出现了一种新的切片缓存文件格式:紧凑型存储(Compact).与之前的松散型存储(Exploded)相比,它有迁移方便.创建更快.减少存储空间等诸多优点,已经成为了现在 ... 
