- Java参数传递的方式只有值传递一种
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。
eg:要想实现引用传递也可以:
使用Wrapper 类(将需要通过方法
调用修改的引用置于一个 Wrapper 类中,再将 Wrapper 对象传入方法)
- 解释为什么函数不能根据返回类型来区分重载
void f() {}
int f() {}
因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。若编译器可根据上下文(语境)明确判断出含义,比如在 int x=f()中,那么这样做完全没有问题。然而,我们也可能调用一个方法,同时忽略返回值;我们通常把这称为“为它的副作用去调用一个方法”,因为我们关心的不是返回值,而是方法调用的其他效果。所以假如我们像下面这样调用方法: f(); Java 怎样判断 f()的具体调用方式呢?而且别人如何识别并理解代码呢?由于存在这一类的问题,所以不能。
函数的返回值只是作为函数运行之后的一个“状态”,他是保持方法的调用者与被调用者进行通信的关键。并不能
作为某个方法的“标识”。
-
equals() 和 hashcode()的关系
两个对象值相同 (x.equals(y) == true) 则该两个对象的hashcode值一定相等,但反过来却不成立Java 对于 eqauls 方法和 hashCode 方法是这样规定的:
(1)如果两个对象相同(equals 方法返回 true),那么它们的 hashCode 值一定要相同;
(2)如果两个对象的 hashCode 相同,它们并不一定相同。 -
还有大家耳熟能详的面向对象的特征(封装、继承、多态、抽象),脑子里都懂但真正让你解释清楚,能说的明白吗?
**1)继承**:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继
承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的
重要手段。
**2) 封装**:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象
的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我
们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程
接口。
**3) 多态性**:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调
用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外
界提供的服务,那么运行时的多态性可以解释为:当 A 系统访问 B 系统提供的服务时,B 系统有多种提供服务的方式,
但一切对 A 系统来说都是透明的。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写
(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做
两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型对
象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
**4)抽象**:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对
象有哪些属性和行为,并不关注这些行为的细节是什么。
- 使用clone()方法的细节
要注意深拷贝和浅拷贝的细节
public class Person implements Cloneable{
privatint age ;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
由于 age 是基本数据类型,那么对它的拷贝没有什么疑议,直接将一个 4 字节的整数值拷贝过来就行。但是 name是 String 类型的, 它只是一个引用, 指向一个真正的 String 对象,那么对它的拷贝有两种方式: 直接将原对象中的 name 的引用值拷贝给新对象的 name 字段, 或者是根据原 Person 对象中的 name 指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的 Person 对象的 name 字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:
下面通过代码进行验证。如果两个 Person 对象的 name 的地址值相同, 说明两个对象的 name 都指向同一个String 对象,也就是浅拷贝, 而如果两个对象的 name 的地址值不同, 那么就说明指向不同的 String 对象, 也就是在拷贝 Person 对象的时候, 同时拷贝了 name 引用的 String 对象, 也就是深拷贝。验证代码如下:
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
String result = p.getName() == p1.getName()
? "clone 是浅拷贝的" : "clone 是深拷贝的";
System.out.println(result);
结果肯定为:clone 是浅拷贝的
所以,clone 方法执行的是浅拷贝, 在编写程序时要注意这个细节。
如何进行深拷贝:
由以上的内容可以得出如下结论:如果想要深拷贝一个对象,这个对象必须要实现 Cloneable 接口,实现 clone方法,并且在 clone 方法内部,把该对象引用的其他对象也要 clone 一份,这就要求这个被引用的对象必须也要实现Cloneable 接口并且实现 clone 方法。那么,按照上面的结论,实现以下代码 Body 类组合了 Head 类,要想深拷贝Body 类,必须在 Body 类的 clone 方法中将 Head 类也要拷贝一份。代码如下:
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable{
public Face face;
public Head() {}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
} }
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head(new Face()));
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
结果为:
body == body1 : false
body.head == body1.head : false
ps:今天就是1024程序员的节日,提前祝各位程序员节日快乐,心想事“橙”!