问一JAVA基础题,下面程序打出来结果是:AB,B
main调用operate(a,b)时,是对象引用的传递,传递a、b的地址给operate,在y=x的操作时,应该能改变b的值,为什么变量b的值没有发生改变呢?
public class T{
public static void main(String[] arg){
StringBuffer a = new StringBuffer ("A");
StringBuffer b = new StringBuffer ("B");
operate (a,b);
System.out.println(a + "," +b);
}
static void operate (StringBuffer x, StringBuffer y) {
x.append (y);
y = x;
}
}
- *
局部变量和成员变量存在堆内存里还是栈内存里?
All local variables (including method arguments) –Stack(所有的局部变量都存在栈内存中)
Objects(including all their fields) –heap(所有的对象包括它们的成员变量都存在堆内存内)
reference在stack里 (引用类型存在栈内存中)
5.2.2方法的参数传递机制
值传递
对于基本类型的参数传递:
public class PrimitiveTransferTest { public static void swap(int a,int b){ int tmp=a; //int a=b;PrimitiveTransferTest.java:4: 错误: 已在方法 swap(int,int)中定义了变量 a //int b=tmp; a=b; b=tmp; System.out.println("swap方法结束后,a的值是:"+a+" ;b的值是:"+b); } public static void main (String[] args){ int a=6; int b=9; swap(a,b); System.out.println("交换结束后,a的值是:"+a+" ;b的值是:"+b); } }
2.对于引用类型的参数传递:
class DataWrap{ int a; int b; } public class ReferenceTransferTest { public static void swap( DataWrap dw){ //dw =new DataWrap(); 会发现a,b并没有被调换,因为在方法内新生成了一个DataWrap对象,因此传入的DataWrap会被覆盖掉。 int dwt=dw.a; dw.a =dw.b; dw.b =dwt; System.out.println(dw.a+" "+dw.b); } public static void main(String[] args){ DataWrap dw=new DataWrap(); dw.a=6; dw.b=9; swap (dw); //this.swap(dw);错误:无法从静态上下文中引用非静态变量。 System.out.println(dw.a+" "+dw.b); } }
5.2.4递归方法
一个方法体内调用它自身,称为方法递归。
例题:*已知有一个数列:f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),求f(10)的值
public class Recursive { public static int fn(int n){ if (n==0) { //不要少写一个等号!!! //fn(0)=1;错误: 意外的类型 return 1; } else if (n==1) { return 4; } else { //int m=2*fn(n-1)+fn(n-2); //return m; return 2*fn(n-1)+fn(n-2); } } public static void main(String[] args){ System.out.println(fn(10)); } }
5.2.5方法重载
- 如果一个类中包含了两个或两个以上方法的方法名相同,但形参列表不同,则被称为方法重载。java允许一个类里定义多个重名方法,只要形参列表不同就可以。
- 和方法的返回值类型、修饰符等无关
- 确定一个方法需要三个要素:
- 调用者:方法的所属者,类或实例。
- 方法名:
- 形参列表:(定义该方法可以接受的参数,用参数类型 参数名表示,多参数之间用“,”隔开,一旦定义方法时指定了形参列表,则调用方法时必须传入对应的参数值)调用方法时,系统会根据传入的实参列表匹配目标方法。
- 确定一个方法需要三个要素:
5.3成员变量和局部变量
成员变量指的是在类里定义的变量
所有变量
- 成员变量
- 实例变量(不以static修饰)
- 类变量(以static修饰)
局部变量
- 形参
- 方法局部变量(在方法内定义)
代码块局部变量(在代码块中定义)
- 类变量从类的准备阶段起开始存在,直到系统完全销毁这个类,类变量的作用域和这个类的生存范围相同;
- 而实例变量从该类的实例被创建时开始存在,直到系统完全销毁这个实例。
- 成员变量
访问成员变量:
- 类.类变量
- 对象.类变量
- 注意java允许通过对象访问static修饰的成员变量本身就是一个错误,在底层上仍然是类来访问类变量。
对象.实例变量
class Person { public static int eyeNum; public String name; } public class TestPerson { public static void main(String[] args){ System.out.println("Person中的eyeNum变量的值是"+Person.eyeNum); Person.eyeNum=2; System.out.println("Person中的eyeNum变量的值是"+Person.eyeNum); Person p = new Person();//不要将new写成大写的形式 System.out.println(p.name); System.out.println("p对象中的name变量的值是"+p.name+"\np对象中的eyeNum变量的值是"+p.eyeNum); p.name="孙浩"; System.out.println("p对象中的name变量的值是"+p.name+"\np对象中的eyeNum变量的值是"+p.eyeNum); Person p2=new Person(); p2.name="李大钊"; System.out.println("p2对象中的name变量的值是"+p2.name+"\np对象中eyeNum变量的值是"+p2.eyeNum); } }
从上面程序可以看出,成员变量无需显式初始化,系统会为其默认初始化。
- 局部变量除了形参之外,都必须显式初始化。
形参的作用域是整个方法体内,且无需显示初始化,其初始化由系统完成。形参的值的由方法调用者负责指定。
不过尽量避免这种情况发生。
public class blockTest{ public static void main(String[] args){ { //int a; blockTest.java:5: 错误: 可能尚未初始化变量a int a=3; System.out.println("局部变量a的值是:"+a); } //System.out.println(a); blockTest.java:8: 错误: 找不到符号 } }
代码块局部变量离开所在的代码块后就立即被销毁
注意一个类中不能定义两个重名的成员变量,即使一个是类变量,另一个是实例变量也不行;一个方法内不能出现两个同名的方法局部变量,注意方法局部变量与形参也不能同名;同一个方法体内不同的代码块局部变量可以同名。
java允许局部变量和成员变量同名,如果方法内的局部变量和成员变量同名,局部变量会覆盖成员变量;如果需要在方法中引用被覆盖的成员变量,需要this(对于实例变量)或类名(对于类变量)来调用。
public class VariableOverrideTest{ private String name="张三"; private static double price=78.0; public static void main(String[] args){ String name="孙悟空"; int price=1; System.out.println(price); System.out.println(name); System.out.println(VariableOverrideTest.price); System.out.println(new VariableOverrideTest().name); } }
5.4隐藏和封装
- 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问;
- 把方法暴露出来,通过方法对这些成员变量进行安全的访问和操作;
5.4.1理解封装
5.4.2使用访问控制符
- private 同一个类中
- default 同一个类中;同一个包中
- protected 同一个类中;同一个包中;子类中
public 同一份类中;同一个包中;子类中;全局范围内
这个Person类实现了良好的封装:
class Person { private String name; private int age; public void setName( String name){ if (name.length()>6||name.length()<2) { System.out.println("你输入的姓名格式有误"); return; } else { this.name=name; } } public String getName(){ return this.name; } } public class PersonTest { public static void main(String[] args){ Person p=new Person(); p.setName("阿姆斯特丹戈尔巴乔夫"); p.setName("Mike"); //System.out.println(p.nama) 错误:name变量是private修饰的,不可以在别的类中直接对其访问。 System.out.println(p.getName()); } }
这段代码中,main()方法不可直接修改Person对象的name和age两个实例变量,只能通过setter方法来操作这个实例变量的值。
一个类常常就是一个小的模块,程序设计时,应该尽量避免一个模块直接操作和访问另一个模块的数据,模块设计追求高内聚和低耦合(仅暴露少量的方法给外部使用)
如果一个java类的每个实例变量都被private修饰,并为每个实例变量都提供了public 修饰的setter方法和getter方法,那么这个类就是一个符号JavaBean规范的类。
对于访问控制符的使用,有以下几条基本原则:
- 类里绝大多数成员变量都应该使用private修饰;工具方法用于帮助实现该类的其它方法,这些方法也应该使用private修饰;
- 如果某个类要用做其他类的父类,该类包含的大部分方法可能仅希望被子类重写,则应该使用protected修饰这些方法;
- 希望暴露出来给其他类自由使用的方法应该用public修饰。
- 外部类通常希望被其他类自由使用,所以大部分外部类使用public修饰。