方法详解
方法是类或对象的行为特征的抽象,方法是类或对象的重要组成部分。Java里的方法不能单独存在,在逻辑上要么属于类,要么属于对象。
方法的所属性:
Java语言是静态的,一个类定义完成后,只要不在重新编译这个类文件,该类和该类的对象所拥有的方法是固定的,永远都不会改变。执行方法时必须使用类或对象来作为调度者。
注意:同一个类中的方法之间相互调度时实际上还是this或者来作为调度者,只不过有时侯会省略,但实际上还是存在。
方法的参数传递机制:
Java中方法传递方式只有一种:值传递。所谓值传递就是将实际参数值的副本传入方法内,而参数本身不会受到任何影响。
package org.westos.practice;
public class valuetransform {
public static void swap(int a,int b){
int tmp;
tmp=a;
a=b;
b=tmp;
System.out.println("a="+a+";"+"b="+b); //a=9;b=6
}
public static void main(String[] args) {
int a=6;
int b=9;
System.out.println("a="+a+";"+"b="+b); //a=6;b=9
swap(a,b);
System.out.println("a="+a+";"+"b="+b); //a=6;b=9
}
}
可以看到最终a,b的值并没有交换,只是传入及交换函数swap的a,b的两个副本值发生交换。
引用数据类型传参:
public class DataWrap {
int a;
int b;
}
测试类,交换a,b的值
package org.westos.practice;
public class DataWrapTest {
public static void main(String[] args) {
DataWrap dw=new DataWrap();
dw.a=6;
dw.b=9;
swap(dw);
System.out.println("a="+dw.a+";b="+dw.b);
}
private static void swap(DataWrap dw) {
int tmp=dw.a;
dw.a=dw.b;
dw.b=tmp;
System.out.println("a="+dw.a+";b="+dw.b);
}
}
/*a=9;b=6
a=9;b=6*/
其中dw作为一个实际参数传入swap函数,其实是采用值传递的方式。复制了dw的副本传入swap,而dw只是一个引用变量,而并没有复制实际对象。所以传入之后相当于在main栈中有一个dw指向实际对象,同时在swap栈中也有一个dw指向实际对象。所以在swap函数中操作的是实际对象,而并非副本。
形参个数可变的方法:
JDK1.5之后,java允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。
实例如下:
package org.westos.practice;
public class Varages {
public static void test(int a,String... books){
//books被当做数组处理
for (String tmp:books){
System.out.println(tmp);
}
//输出整形变量a的值
System.out.println(a);
}
public static void main(String[] args) {
test(5,"Java","c++","python");
}
}
/*Java
c++
python
5*/
可以看出形参个数可变的参数本质就是一个数组参数
public static void test(int a,String... books)
public static void test(int a,String[] books)
这两段代码效果完全一样,但是调用的时候存在差别,形参个数可变的这种方法调用更加简洁。但是它只能放在形参最后传入,而数组可以放在任意位置传入。
- 类名作为形参
先定义一个类,在测试类中再定义一个需要类传入的方法;
Animal.class
package org.westos.practice;
public class Animal {
public void eat(){
System.out.println("我是普通类的方法");
}
}
- 抽象类作为形参
同上
AbsAnimal.class
package org.westos.practice;
public abstract class AbsAnimal {
public abstract void show();
}
Cat类实现该抽象类;
Cat.class
package org.westos.practice;
public class Cat extends AbsAnimal{
@Override
public void show() {
System.out.println("我是抽象类子类的重写方法");
}
}
- 接口类作为参数传递
MyInterface.class
package org.westos.practice;
public interface MyInterface {
void intershow();
}
Dog类实现该抽象类;
Dog.class
package org.westos.practice;
public class Dog implements MyInterface {
@Override
public void intershow() {
System.out.println("我是接口子类实现的方法");
}
}
测试类中:
package org.westos.practice;
public class MyTest {
public static void main(String[] args) {
//如果方法需要传入一个类,你就传入这个类的对象
methodClass(new Animal());
//如果方法需要传入一个抽象类,你就传入这个抽象类子类的对象
//Cat类实现了抽象类的方法
//AbsAnimal absan= new Cat();
methodAbsClass(new Cat());
//如果方法需要传入一个接口类,你就传入这个接口类实现的子类对象
//Dog类实现了接口方法
methodInterClass(new Dog());
}
public static void methodClass(Animal an){
an.eat();
}
public static void methodAbsClass(AbsAnimal absan){
absan.show();
}
public static void methodInterClass(MyInterface inface){
inface.intershow();
}
}
成员变量和局部变量
注意事项:
- 成员变量又可分为类变量(被static修饰)和实例变量(没有被static修饰)。
类变量随着类的创建而创建,销毁而销毁;这个类普遍相同的属性,如人都有两个眼睛,一个嘴巴。
实例变量随着对象的创建而创建,销毁而销毁,对于同一个类,不同对象属性不同,如每个人的身高体重。 - 在一个方法中同名的局部变量会覆盖成员变量,不过可以通过this或指定类名作为调用者来访问被覆盖的成员变量。不过大部分时候应该避免这种重名情况。
- 在程序中使用局部变量,应该尽可能的缩小局部变量的范围,局部变量的作用范围越小,它在内存中停留的时间就越短,程序运行性能就越好。
深入构造器
构造器是一个特殊的方法,这个方法用于创建实例时执行初始化。构造器是创建对象的重要途径。
使用构造器执行初始化
当创建一个对象时系统为这个对象的实例变量进行默认初始化,这种默认的初始化把所有基本类型的实例变量设为0或false。把所有引用类型的实例变量设为null。如果想改变这种默认的初始化,想让系统创建对象时就为该对象的实例变量显式指定初始值,就可以通过构造器来实现。
注意:如果程序员没有为java提供任何构造器,则系统会为这个类提供一个无参数的构造器,这个构造器为空,不做任何事,但无论如何如何java类至少包含一个构造器。
package org.westos.practice1;
public class ConstructorTest {
public String name;
public int count;
//提供自定义的构造器,该构造器包含两个参数
public ConstructorTest(String name,int count){
//构造器里的tjis代表它进行初始化的对象
//将传入的两个参数赋值给this代表对象的name和count
this.name=name;
this.count=count;
}
public static void main(String[] args){
//系统将会对该对象执行自定义的初始化
ConstructorTest tc=new ConstructorTest("java",3);
System.out.println(tc.name);
System.out.println(tc.count);
}
}
注意:虽然说构造器是创建对象的途径,但实际上当程序员调用构造器的时候,系统先为该对象分配内存空间,并为这个对象执行默认初始化,这个对象已经产生了,这些操作在构造器执行之前就都完成了。也就是说,当系统开始执行构造器之前,系统已经创建了一个对象,只是这个对象还不能被外部程序访问,只能在该构造器中用this来引用。当构造器执行完之后,这个对象会作为构造起的返回值被返回,通常会赋给另一个引用变量,从而让外部程序可以访问该对象。
通常把构造器设置成public访问权限,从而允许系统中的任何位置的类都能创建该类的对象。
构造器重载
构造器重载和之前的方法重载一样,每个重载的构造器使用与类相同的名字。只是需要传入的参数个数和类型不一样;
实例如下:
package org.westos.practice1;
public class Constructer {
String name;
int age;
//默认的构造器,由于还要定义其他重载构造器,所以得让他显式,而不被覆盖
public Constructer(){
}
public Constructer(String name){
this.name=name;
}
public Constructer(int age){
this.age=age;
}
public static void main(String[] args) {
Constructer ct=new Constructer();
System.out.println(ct.name);
System.out.println(ct.age);
/*null
0*/
String name="Liming";
Constructer ct1=new Constructer(name);
System.out.println(ct1.name);
System.out.println(ct1.age);
/*Liming
0*/
int age=4;
Constructer ct2=new Constructer(age);
System.out.println(ct2.name);
System.out.println(ct2.age);
/*null
4*/
}
}
本例使用方法重载一共创建了三个对象。