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’}; 这里的s和chs打印输出的不是地址值而是对应的元素本身. 这个地方说明一个问题,假如你自定义的对象重写了.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方法。 所有的包装类(Integer,Boolean等)都已经重写了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代表的就是被遍历的对象的元素.
|