时间:2018-07-19
作者:魏文应
一、成员变量和局部变量
成员变量,也叫做 类的属性。下面代码中, age
和 name
是声明在 类里,但在方法外 的变量,那它就叫做 类的成员变量。相对的,变量 i
、 j
、 n
依次是声明在 方法内、方法的形参、代码块内 的三个变量,这三种情况的变量,叫做 局部变量 。
class Animal{
// 这就是属性(成员变量)
int age;
String name;
public void setAge(int n){
// 这就是局部变量
int i = 0;
for(int j = 0; j < 10; j++)
i++;
age = n;
age += i;
}
}
成员变量 存储在堆 中,局部变量 存储在栈 中。比如,下面代码中,Animal 对象 test 的成员变量,有age和name,分别存储在堆中,test 存储了一个地址引用,指向堆中Anima类的实体。而 i、n、j 存储在堆中:
public class TestAnimal{
public static void main(String[] args){
Animal test = new Animal();
test.setAge(1);
}
}
局部变量 必须显式初始化,成员变量可以 不显式初始化,编译器可以给 成员变量 一下默认赋值:
数据类型 | 默认赋值 |
---|---|
byte、short、int、long | 0 |
float、double | 0.0 |
char | 空格 |
引用类型变量(比如String) | null |
成员变量可以使用 public、private、protected、缺省
修饰变量,比如:
public int age;
局部变量却不可以使用这些关键字来修饰(修饰了没有意义)。
二、类的方法
方法格式:权限修饰符 返回值类型 方法名 参数
:
class Animal{
public void testPrintln(){
System.out.println("Hello World!");
}
}
可以在方法内 调用其它方法,但不能在方法内 定义其它方法。
三、方法的重载
构成方法的重载,需要下面的条件:
- 在同一个类中。
- 方法名必须相同。
- 方法的参数列表不同。
class OverLoad{
public int getSum(int i, int j){
return i + j;
}
public int getSum(int i, int j, int k){
return i + j + k;
}
public double getSum(double d1, double d2){
return d1 + d2;
}
public void getSum(double d1, double d2, double d3){
System.out.println(d1 + d2 + d3);
}
}
从上面可以看出,方法重载 和返回值没有关系。比如下面会 报错:
class OverLoad{
public int getSum(int i, int j){
return i + j;
}
public void getSum(int i, int j){
return i + j;
}
}
四、匿名类对象
匿名,就是没有名字。下面代码中,TestNoName c = new TestNoName();
,这个是 有名称 的类对象,名称为 c 。new TestNoName().show();
这个是 没有名称 的类对象,这就是匿名类对象。
public class PassObject {
public static void main(String[] args) {
TestNoName c = new TestNoName();
new TestNoName().show();
}
}
class TestNoName {
public void show(){
System.out.println("Test No Name Class");
}
}
当我们只需 调用类对象一次,那我们就可以考虑使用匿名类对象。
五、可变形式形参的方法
方法的参数个数是可变的,可以是任意多个:0个到无穷多个。格式如下:
修饰 返回值 函数名 (参数类型 ... 参数名)
或者 使用下面格式
修饰 返回值 函数名 (参数类型[] 参数名)
比如,下面两种格式是一样的,表示一模一样的一个方法:
public void sayHello(String ... args)
public void sayHello(String[] args)
下面是一个应用示例(其实常见的 main(String[] args)
就是可变参数):
public class TestArgs {
public static void main(String[] args) {
new TestHello().sayHello("Hello", "World", "!");
}
}
class TestHello{
public void sayHello(String ... strings ){
for(int i = 0; i < strings.length; i++){
System.out.println(strings[i]);
}
}
}
当我们有一个固定参数的时候,我们需要先把固定参数写前面,可变参数写后面。比如下面的代码,参数 i 写前面,可变参数写后面:
public void sayHello(int i, String ... strings)
六、函数参数值传递
方法参数传递中,我们可以看下面代码:
public class TestSwap {
public static void main(String[] args) {
int i = 1, j = 2;
TestSwap tmp = new TestSwap();
tmp.swap(i, j);
System.out.println("i = " + i + "; " + "j = " + j + ";");
}
public void swap(int m, int n){
m = m ^ n;
n = m ^ n;
m = m ^ n;
System.out.println("m = " + m + "; " + "n = " + n + ";");
}
}
运行打印的结果显示,m 和 n 的值发生了互换,但 i 和 j 的值没有发生互换。原因是,当我们传参时 swap(i, j)
,栈中就会开辟新的两个内存空间,并赋值 m = i; n = j;
。从图中可以看出,i 和 j 的值 从始至终没有改变过,变化的只是 m 和 n。
如果想要上面的 i 和 j 的值发生交换,类方法的参数应该使用 引用数据类型:
public class TestArgsSwap {
public static void main(String[] args) {
DataSwap tmp = new DataSwap();
tmp.i = 1;
tmp.j = 2;
TestArgsSwap tmp2 = new TestArgsSwap();
tmp2.swap(tmp);
System.out.println("tmp.i = " + tmp.i + "; " + "tmp.j = " + tmp.j + ";");
}
public void swap(DataSwap ds){
ds.i = ds.i ^ ds.j;
ds.j = ds.i ^ ds.j;
ds.i = ds.i ^ ds.j;
System.out.println("ds.i = " + ds.i + "; " + "ds.j = " + ds.j + ";");
}
}
class DataSwap{
int i;
int j;
}
由于类方法参数 ds ,同样指向 tmp 类的堆内存地址,两个 引用变量指向的内存相同,所以导致最终 tmp.i 和 tmp.j 的值被修改了。
七、面向对象的封装性
我们是可以 直接给成员变量赋值 的,比如,下面的代码,给类成员变量 legs 赋值:
public class TestAnimal {
public static void main(String[] args) {
Animal tmp = new Animal();
tmp.legs = 4;
}
}
class Animal{
String name;
int legs;
}
但是,我们希望对legs的值的范围做一定的限制,那么使用类方法可以对属性进行操作:
public class TestAnimal {
public static void main(String[] args) {
Animal tmp = new Animal();
tmp.legs = 4;
tmp.setLegs(3);
}
}
class Animal{
String name;
int legs;
public void setLegs(int n){
if(n > 0 && n % 2 == 0){
legs = n;
}else{
System.out.println("你输入的数据有误");
}
}
}
这时我们还是可以 在外面对 legs 成员变量 进行赋值操作,如果我们 不希望外面能够直接 对legs进行赋值操作,那么,可以在成员变量前,加上 private
私有化修饰:
public class TestAnimal {
public static void main(String[] args) {
Animal tmp = new Animal();
// tmp.legs = 4; // 这就不可以用了。否则报错
tmp.setLegs(3);
}
}
class Animal{
String name;
private int legs;
public void setLegs(int n){
if(n > 0 && n % 2 == 0){
legs = n;
}else{
System.out.println("你输入的数据有误");
}
}
}
八、权限修饰符
Java权限修饰符 public、protected、private、缺省,置于类的成员定义前,用来 限定 对象对该类成员的 访问权限 。权限范围如下(yes表示可以访问):
修饰符 | 类内部 | 同一个包 | 子类 | 任何地方 |
---|---|---|---|---|
private | yes | |||
(缺省) | yes | yes | ||
protected | yes | yes | yes | |
public | yes | yes | yes | yes |
上面的四个中修饰符情况,可以修饰 类的方法 和 类的成员。对于 class的权限修饰 只可以用 public 和 (缺省):
- public类可以在任意地方被访问。
- default类只可以被同一个包内部的类访问。
九、类的成员之三:构造器
类除了属性、方法,还有 构造器(constructor 构造方法) 。它的形式如下:
class Person{
public Person(){
}
}
也就是,它和 它所在的类重名 。如果没有没有在类中 显示地定义构造器,编译器会默认给一构造器。默认的构造器也就是上面 空参形式的构造器 。我们可以 自定义构造器,构造器也可以 重载:
class Person{
private String name;
public Person(){
}
public Person(String n){
name = n;
}
}
十、this 关键字
this 关键字,表示 当前类。比如下面的示例,方法中的 参数变量名为name,而当前 类成员变量 也有一个变量名为name 。这个时候,同时名为name的这两个变量,就 不能区分哪个是哪个。所以就加上this修饰,用 this.name
表示成员变量 private String name
的name。
class Person{
private String name;
public void setName(String name){
this.name = name;
}
}
this 可以像上面一样修饰 成员变量,同样也可以修饰 类方法、构造器。