黑马程序员技术交流社区

标题: 关于String类对象级char[]数组输出的不是地址值问题及新型fo... [打印本页]

作者: 黄马良    时间: 2017-12-26 23:26
标题: 关于String类对象级char[]数组输出的不是地址值问题及新型fo...
1.为什么char[]数组和String类型对象直接输出是元素值而不是地址值,地址值又代表了什么含义?
基本数据类型和对象引用
基本数据类型会一直在栈中创建,当声明基本类型时,不需要new
int a = 10;
栈的读取速度比堆快。基本类型一旦被声明,java将在栈上直接存储它,所以基本类型的变量表示的是数据本身。
假如调用基本类型的包装类来创建对象,那么将会在堆中创建。
Student s = new Student();
等号右侧的new Student() 。这个new是在内存的堆中为对象开辟空间,保存对象的数据和方法。等号左侧 Student s。s指代的是Student的一个对象,称为对象引用,这个对象引用是在栈中创建的。实际上a不是对象本身,它用来指向一个地址。赋值=。这个就是把对象的地址赋给a。此时输出a就是一个内存地址。
那么为什么
String s = new String(“string”)
char[] chs = new char[]{‘a’,’b’,’c’};
这里的schs打印输出的不是地址值而是对应的元素本身.
这个地方说明一个问题,假如你自定义的对象重写了.toString方法,此处就会显示你的自定义的重写方法的输出值。
java的基本类型包装类中就重写了这个方法,所以调用print方法时会自动调用它的toString()方法。
如下程序我先定义了一个Student类和Teacher类分别在测试类中创建对象,并打印
    public static void main(String[] args) {
                Student s = new Student("张三", 10, 20);
                Teacher t = new Teacher("李四", "男");
                System.out.println(s);
                System.out.println(t);
        }
我在Student类中重写了toString()方法,Teacher类中什么也没做
@Override
        public String toString(){
                return ("名字是" + name + "----" +"年龄是" + age +"-----" + "分数是" +                 score  );
        }
下面可得结果为
名字是张三----年龄是10-----分数是20
com.itheima.Teacher@b03be0
前者仍然是内存地址,后者则是我重写的方法。
print方法在调用事,假如类中的toString()方法没有被重写,则会电泳String.valueof()方法(这个这里暂不讨论),假如重写了就会调用toString方法。
所有的包装类(IntegerBoolean等)都已经重写了toString方法,所以不会输出内存地址,而是输出正确的值。
再来看基本类型的数组输出值
我在这里创建了并初始化了全部基本类型的数组并打印
public static void main(String[] args) {
                byte[] a = {1,2,3};
                System.out.println(a);
                short[] b = {1,2,3};
                System.out.println(b);
                int[] c = {1,2,3};
                System.out.println(c);
                long[] d = {1,2,3};
                System.out.println(d);
                double[] e = {1,2,3};
                System.out.println(e);
                float[] f = {1.1f,1.2f,1.3f};
                System.out.println(f);
                boolean[] g = {true,false};
                System.out.println(g);
                char[] h = {'a','b','c'};
                System.out.println(h);
        }
下面是输出的结果:
[B@3fa5ac
[S@95cfbe
[I@179dce4
[J@1950198
[D@19bb25a
[F@da6bf4
[Z@1e58cb8
abc
为什么所有类型的数组中只有char的数组类型打印出了正确的结果而没有输出内存地址?
这是因为对于int等其他类型的数组被java认为是一个对象,调用的是下面的方法:
public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }
接着来看valueOf(x)方法:
public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }
然后再进入obj.toString()的源码查看:
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
通过这个源码可以看出为什么输出会是[I@179dce4了,I代表的类的名称(其他类型的同理)
接下来再看char[]调用的源码:
public void println(char x[]) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }
这里的char x[] 相当于字符数组的初始化即char[] x;
再通过源码查看  print(x)方法:
  public void print(String s) {
        if (s == null) {
            s = "null";
        }
        write(s);
    }
接着再窥探  write(s)里的奥秘:
private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }
用过查看pintln()方法的源码我们可以知道,由于其他类型的数组都被认为是Object类型了,所以会输出内存地址。而char[]调用的方法是输出char这个数组中的每一个值,所以不是内存地址了。(额具体内部的源码我还看不太懂,但至少可以看出区别)
2.for循环在Java中的应用
通过十几天的基础班的学习,我们学会了for循环的基本使用其基本格式为
for(初始化表达式;条件表达式;循环后的操作表达式) {
                        循环体;
                }
还有一个for的死循环的格式
for(;;){
循环体
}
但其实for循环在java中还有更多的应用
(1)遍历数组的传统方式
数组的遍历
int[] a = {1,2,3,4};
        for (int i = 0; i < a.length; i++) {
                System.out.println(a);
        }
集合专用的迭代器遍历
ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        Iterator<String> it = list.iterator();
        for(;it.hasNext();){
                String s = it.next();
                System.out.println(s);
        }
(2)而在Java语言的最新版本――J2SE 1.5中,引入了另一种形式的for循环。借助这种形式的for循环,现在可以用一种更简单地方式来进行遍历的工作。
基本格式
for(循环变量类型 循环变量名称 : 要被遍历的对象){
循环体
}
现在数组和集合的遍历可以这样写
集合
ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        for(String i : list){
                System.out.println(i);
        }
数组
int[] a = {1,2,3,4,5};
        for(int i : a){
                System.out.println(i);
        }
由上可知,这里的循环变量类型与要被遍历的对象的元素类型相同,并且循环变量i代表的就是被遍历的对象的元素.


作者: 小浙姐姐    时间: 2017-12-30 16:57
这些内容在后面都是会提到的,第二种循环方法叫“增强for循环”,有利有弊,以后都会学到的哈!你现在在学习中如果遇到这些问题也都可以问你的导师或者助教呢!我们很欢迎你们去挖掘这些,多学习,多思考,都有助于以后的工作呢!加油哦!
作者: 秋子菇凉    时间: 2018-1-7 11:12
不错,知识点很详细




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2