向Java程序员的目标前进!
day11
面向对象—续
面试题:局部变量和成员变量的区别
-
书写位置不同
局部变量:在方法定义中或方法声明上,范围较小
成员变量:在类中方法外,范围较大
-
内存中的位置不同
局部变量:在栈内存中
成员变量:在堆内存中
-
生命周期不同
局部变量:随着方法调用而存在,随着方法调用结束而消失
成员变量:随着对象创建而存在,随着对象使用完毕等待垃圾回收器空闲时回收时而消失
-
初始化值不同
局部变量:有两种方式。要么先定义再初始化;要么定义时直接初始化
注意:局部变量在使用之前必须初始化,否则会报错"可能尚未初始化变量"
成员变量:系统有默认初始化机制,可以不初始化。但是一般不会直接赋值,都是通过"对象名.成员变量名=数据值"的方式赋值。
面试题:不同类型形参的改变对实参的影响
- 形式参数如果是基本数据类型:形参的改变不会影响实际参数
- 形式参数如果是引用数据类型(数组、类、接口):形参的改变会直接影响实际参数
- String类型是一种特殊的引用类型,它作为形参的效果和基本数据类型是一致的,形参的改变不会影响实际参数。
解析:
-
形式参数如果是基本数据类型,它在定义变量时会在栈内存单独开辟空间。然后将定义的变量做为形参传送数据值并调用相应方法时,也会在栈内存中开辟空间。但是,成员方法在执行结束后,方法内部的局部变量也会随之消失。所以形参的改变并不会影响实际参数。
-
形式参数如果是引用数据类型,它在定义变量时会在栈内存单独开辟空间。但是它作为引用数据类型也会在堆内存中开辟空间并将数据存储在堆内存空间中。栈内存的空间会以指针的形式指向堆内存开辟的对应空间(就像C语言的指针那样,因为Java的底层由C实现)。然后将定义的变量做为形参传送地址值并调用相应方法时,方法中的运算会通过地址直接操作堆内存中的数据值。成员方法在执行结束后,方法会随之消失。但是堆内存中的对象还会继续存在,直到使用完毕后被垃圾回收器(Garbage Conllection,Java特有的一种机制,以后会介绍)回收。所以形参的改变会直接影响实际参数。
-
String类型是一种特殊的引用类型,它的数据值在常量池中存储,且一旦创建后就不能再改变。它作为形参的效果和基本数据类型是一致的,形参的改变不会影响实际参数。
由于刚开始学Java,目前对一些内部机制和常量池仅知道一点点皮毛,这里只能简单说说。如果写的有什么问题,欢迎大家批评指正。
补充:
形式参数如果是类,实际参数需要传递的是当前类的具体类型。因为类也是引用数据类型,在调用时要传递空间地址值。
形式参数面试题的练习:
/**
* 看程序,写结果
*/
class ClassA{
int value;
}
public class TestClassA{
public static void main(String args[]){
int value = 10;
changeInt(value);
System.out.println(value);
ClassA ca = new ClassA();
ca.value = 10;
changeObject(ca);
System.out.println(ca.value);
}
public static void changeInt(int value){
value++;
}
public static void changeObject(ClassA ca){
ca.value++;
}
}
编译运行TestClassA 时,结果是
A. 编译出错
B. 输出 10 10
C. 输出 10 11
D. 输出 11 11
解析:
首先看classA。classA中定义的成员变量存储于堆内存中,所以它跟当前类的对象有关,默认值是0。接着是TestClassA,定义了int类型(基本数据类型)的变量,之后调用了changeInt(),由于并没有改变main方法中value的值,所以仍然输出10。
然后程序中new了一个对象(引用数据类型),使用对象直接赋值了10,之后调用了changeObject(),由于改变了main方法中value的值,所以输出11。
匿名对象
匿名对象,顾名思义就是没有名字的对象。匿名对象不需要在栈内存开辟空间,直接在堆内存申请空间。
格式:
new 类名();
使用匿名对象的好处:
-
在实际开发中,由于没有栈内存变量指向堆内存地址,匿名对象在使用完毕之后的空间会立即被垃圾回收器回收。但是不要多次使用匿名对象,一般使用一次就好。
-
匿名对象可以作为参数传递
-
从内存的角度考虑,匿名对象可以节省内存。
封装—续
setter和getter方法
将一个类中的所有属性都加入private关键字(私有化),提供对外的公共setXXX()和getXXX()访问。
this关键字
this关键字指当前所在类的对象的地址值引用。当局部变量隐藏了成员变量时,this用来区分局部变量和成员变量。其中,“this.变量” 表示成员变量。接下来通过内存图解this:
为了清楚地展示setter和getter及this的使用,我们继续改进昨天封装的最开始的例子。
/**
* 需求:描述一个学生事物,有姓名,和年龄两个属性
* 然后写一个测试类,测试学生的信息
*/
class Student{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
//public void setAge(int age) {
// this.age = age;
//}
public void setAge(int age) {
if(age<0 || age> 120){
System.out.println("年龄数据非法!");
}else{
this.age = age;
}
}
}
class StudentTest{
public static void main(String[] args){
Student student = new Student();
student.setName("爱学cs的小陈");
student.setAge(-45);
System.out.println("学生的姓名是:"+student.getName+",年龄是:"+student.getAge);
System.out.println("----------------------------");
student.setAge(18);
System.out.println("学生的姓名是:"+student.getName+",年龄是:"+student.getAge);
}
}
补充:
在使用IntelliJ IDEA开发时,使用快捷键实现setter和getter方法的自动创建是再方便不过的事了。自动创建setter和getter方法的快捷键是ALT+INSERT组合键(有的键盘需要Fn+ALT+INSERT一起组合),然后选择"Getter and Setter",按提示操作就可以了。