Java基础之面向对象的方法参数和对象上转型

 本篇博客着重说明方法的参数传递机制和 对象上转型,作为前几篇博客的续貂之作,当然面向对象我还没有回顾完呢。言归正传。

   

一、方法的参数传递机制

1.1说明:java里的方法不能单独存在,调用方法必须使用类或者对象作为主调者。

如果声明的方法有形式参数声明,则在调用时必须指定这些形式参数的实际值

 

那么java的参数实际值是怎么传递到方法体内部的呢?

 首先说一下:java的方法的参数传递方式只有“值传递”一种,

值传递:将实际参数值的副本(复制品)传入方法内部。而参数本身不会受到影响。

参看下面的代码:

实例代码1;

public class PrimitiveTransferTest {
		public static void main(String[] args) {
			int a = 9;
			int b = 10;
			System.out.println("原值: a = "+a+" b = "+b);
			swap( a , b);
			System.out.println("交换后的原值: a = "+a+" b = "+b);
		}
		public static void swap(int a ,int b){
			int temp = a;
			a = b;
			b = temp;
			System.out.println("参数的副本交换 :  a = "+a+" ,  b = "+b);
		}
}

运行结果是a,b在main函数里的值并没有交换,但是swap里的参数值却交换了,但是swap却没有达到它应该有的功能,是因为程序执行swap方法的时候,为该方法传递的是a,b变量的副本,而不是a,b,变量的本身,进入swap方法后系统产生了四个变量,a,b本身在main方法所在的栈区里,而a,b的副本在swap方法的栈区中,swap的功能仅仅是交换自己栈区的a,b的值。

这个例子很经典,也很容易令初学者犯错,其实是很简单的,但是如何能让swap达到可以交换main方法里的a,b的值呢,大家应该能想到用指针可以实现这个交换操作,。

怎么使用指针呢,a,b是基本数据类型,只有直接调用的方式,而main方法中的a,b值是局部变量,因此可以在main方法中直接进行a,b的交换(有时候在算法设计时该交换是单独作为一个函数出现的),或者将a,b放入一个引用类型的数据类型里。

解决方法一:

 a = a^b;b = a^b;a= b^a;

这个是交换两个数最快也最省事的了。

   解决方法二:该方法需要两个空间的数组,因为数组是引用类型的,因此可以使用该方法,传递的是一个int类型的数组。

实例代码2;

int arr[] = new int[2];
arr[0] = a;
 arr[1] = b;
 swap(arr);

public static void swap(int array[]){
			array[0] = array[0]^array[1];
			array[1] = array[0]^array[1];
			array[0] = array[1]^array[0];
			//System.out.println("交换后的参数2:arr[0] = "+arr[0]+" arr[1] = "+arr[1]);

		}	
解决方法三:
	   Info info = new Info();
			System.out.println("交换前: "+info.a+"  ----  "+info.b);
			 swap(info);
			 System.out.println("交换后: "+info.a+"  ----  "+info.b);

/*
		 * 第三种方式使用class 将 a,b的值存入,然后方法内使用Info 的实例,进行交换
		 */
		public static void swap(Info info){
			int temp = info.a;
			info.a = info.b;
		    info.b = temp;
		}

对于第二种和第三种方式的解释:由于采用了引用类型的数据进行传值,所传的便是真正的值本身,而不是值的一个副本,

系统复制的只是引用数据指针,而不是引用数据指向的数据,因此在程序执行的时候main方法创建了一个Info的对象,并定义了一个info引用变量来指向Info对象,这是一个与基本类型不同的地方,创建一个对象时,系统内存中有两个东西:堆内存中保存了对象本身,栈内存保存了引用该对象的引用变量。在swap方法交换时,只是利用引用变量的副本实现指向两个变量的功能,并在swap函数里进行交换。

 可以将第三种方式里的info参数赋值为null;

 即 info = null;

再在主函数里进行info调用a,b的值,可以发现不会报错,

下面是这个例子的完整实例:提供了三个重载swap的方法。

实例代码3;


public class PrimitiveTransferTest {
		public static void main(String[] args) {
			Info info = new Info();
			System.out.println("交换前: "+info.a+"  ----  "+info.b);
			 swap(info);
			 System.out.println("交换后: "+info.a+"  ----  "+info.b);
		}
		public static void swap(int array[]){
			array[0] = array[0]^array[1];
			array[1] = array[0]^array[1];
			array[0] = array[1]^array[0];
			//System.out.println("交换后的参数2:arr[0] = "+arr[0]+" arr[1] = "+arr[1]);

		}
		public static void swap(int a ,int b){
			int temp = a;
			a = b;
			b = temp;
			System.out.println("参数的副本交换 :  a = "+a+" ,  b = "+b);
		}
		/*
		 * 第三种方式使用class 将 a,b的值存入,然后方法内使用Info 的实例,进行交换
		 */
		public static void swap(Info info){
			int temp = info.a;
			info.a = info.b;
		    info.b = temp;
		}


}
class Info{
	int a = 10;
	int b = 11312;
}

1.2形参个数可变的方法。

说明:从JDK 1.5之后,java允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。

实例代码4;

package cn.com.basicTwo;
public class Varargs {
	//形参数目可变的方法
	public static void test(int a,int b,String ... books){
		//这种形式的是增强for循环形式
		for(String book: books){
			System.out.println(book);
		}
	}
	public static void test(int param[]){
		for(int i =0;i<param.length;i++){
			System.out.println(param[i]);
		}
	}
	public static void main(String[] args) {
		Varargs.test(1, 2, "abcd","efds");
		System.out.println("\n\n");
		
		Varargs.test(3, 4, "asdfawe","sdfwewr","sdf","asdwe");
		System.out.println("\n\n");
		
		int array []= new int [4];
		array[0]= 130;
		array[1] = 102;
		array[3]= 2425;
		Varargs.test(array);
	}
}

上面使用两种方式指定形参数目不同,可以满足一定特殊的需求。

 

注意:长度可变的形参只能出现在形参列表里的最后,一个方法里最多只能包含一个长度可变的形参,调用包含一个长度可变的形参方法时,这个长度可变的形参既可以传入多个参数,也可以传入一个数组。

不过数组形式的可变参数跟普通参数一样,没有什么特殊要求。

1.3递归方法

 说明:一个方法体内调用它自身,被称为方法递归。方法递归包含了一种隐士的循环,会重复执行某段代码,但这种重复执行无需循环控制。

实例代码5:

public class TestRec {
	//求和(有限递归),根据返回值递归退出循环
	public static int getSum(int x){
		if(x == 1){
			return 0;
		}else{
			return getSum(x-1)+x;
		}
	}
	/**
	 * 
	 * 作者:FCs
	 * 描述:这种方式没有返回值重复调用自己,
	 * 循环不能终止,这种方式类似于死循环,将不断的申请栈空间最终导致内存溢出。
	 * 尽量不要这样用。而且这种方法非常危险。
	 * 时间:2014年8月18日
	 * 返回:void
	 */
	public static void getInfo(){
		System.out.println("我是不好的代码,循环调用自己。。。。");
		getInfo();
	}
	
	public static void main(String[] args) {
	 int  a = getSum(5);
	 System.out.println(a);
	   // getInfo();
	}
}	

说明:一般使用递归都是有某种返回值的,当满足返回条件的时候,递归会逐层返回,就像栈一样,每一次调用自己都会在自己的栈区里申请一块内存,用指针指向它,这种方法非常消耗内存,一般可以使用递归的情况都可以使用非递归形式进行递归的消除,但是使用非递归的形式进行消除是一种非常困难的时候,代码难懂,尤其是在算法方面,因为递归的形式比非递归的形式更加简洁明了。

 

 二、对象上转型,下转型(为下一章做个引子先)

对象上转型属于java多态的内容,但是先在这里总结一下。

定义: 父类声明,子类实例化的对象称为上转型对象。该对象是子类的简化,不关系子类的新增功能,只关心子类继承的和重写的功能。

这种类型通常见于父类与子类之间

形式:

上转型对象: 父类   name = new 子类();

下转型对象: 子类   name = (父类)子类name;

说明:下转型对象需要强制转型,

上转型对象的特点

1.上转对象不能操作子类新增的成员变量,失掉了这部分属性,不能使用子类新增的方法,失掉了一些功能。

2.上转型对象可以操作子类继承的成员变量,也可以使用子类继承的或重写的方法。

3.如果子类重写了父类的某个方法后,当对象的上转型对象调用这个方法时一定是调用了子类重写的方法。因为程序在运行时知道,这个上转对象的实例是子类创建的,只不过损失了一些功能而已。

注意:
1.可以将对象的上转型对象再强制转换为一个子类对象,这时,

该对象又具备了子类所有属性和功能。

2.不可以将父类创建的对象赋值给子类声明的对象。

实例代码6:

package cn.com.basicThree;
/**
 * 
 * @author fcs
 * 2014年8月18日
 * Computer
 * 说明:对象上转型,与下转型
 */
public class Computer{
	public String  name = "重量";
	public String  band = "品牌";
	public String getInfo(){
		return name+" -- "+band;
	}
	//父类特有方法
	public void  getBand(){
		System.out.println(band);
		
	}
	public static void main(String[] args) {
		Computer computer = new Lenovo();   //上转型对象
		System.out.println(computer.band);  //上转型对象调用从父类继承的变量,该对象不能调用自己的变量
		System.out.println(computer.name);
		computer.getInfo();                 //上转型对象调用从父类重写的方法的方法,该对象不能调用自己的增加的方法
	                                        //computer.getCom();报错
		computer.getBand();                 //调用父类未被重写的方法
		
		Lenovo  lenovo = (Lenovo) computer;  //将上转型对象再转回子类对象(父类下转型),跟普通子类对象具有相同功能特点。
		System.out.println(lenovo.band);     //调用父类变量
		System.out.println(lenovo.price);    //调用自己的变量
		lenovo.getBand();
		//lenovo.getInfo();      这种方式不建议使用,即当上转型对象再转回子类对象的时候,不要再调用被子类重写的方法。
		lenovo.getCom();   //调用子类新增方法
		//		Computer comp = new Computer();
//		Lenovo  lenv = (Lenovo)comp;   编译报错? 请读者思考
		//System.out.println(lenv.band);
		//System.out.println(lenv.price);
		//lenv.getBand();
		//System.out.println(lenv.getCom());
		//lenv.getInfo();
	}
}
class Lenovo extends Computer{
	public String  xinghao = "G480";
	public double   price = 3453.89;
	//重写父类方法
	public String getInfo(){
		return xinghao+" -- "+price;
	}
	//子类新增方法
	public String getCom(){
		return "Lenovo";
	}

}

上转型对象扩展了父类的使用范围和灵活度,因为子类对父类的方法在某种程度上进行了优化,因此在这种形式下上转型对象可以最大化的使用从父类继承的优点。可以使用父类对各个继承自己的子类进行这种方式的变化,进而体现了多态的特性。

 

三、instanceof 运算符说明

  Instanceof的运算符的前一个操作数通常是一个引用类型的变量,后一个操作数通常是一个类或者接口,

作用:首先用于判断前面的对象是否是后面的类的实例,然后是否可以成功进行类型转换,从而使程序更加健壮。

说明:整个表达式的值要么是true,要么是false

使用:instanceof运算符前面操作数的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系。否则编译时出错。

实例代码7:


package cn.com.basicThree;
/**
 * 
 * @author fcs
 * 2014年8月19日
 * Instanceof
 * 说明:instanceof功能说明
 * 
 * 
 */
public class Instanceof {
	public static void main(String[] args) {
		//声明Hello的时候使用Object类。则Hello的编译类型是Object
		//Object是所有类的父类,但Hello变量的实际类型是String 
		
		Object hello = "hello";
		//String是Object的子类返回true 
		System.out.println("字符串是否是Object类的实例:"+(hello instanceof Object));
		System.out.println("字符串是否是 String类的实例: "+(hello instanceof String));
		System.out.println("字符串是否是Math类的实例: "+(hello instanceof Math));
		//String 实现了Comparable接口,返回true
		System.out.println("字符串是否是Comparable接口的实例: "+(hello instanceof Comparable));
		String a = "hello";
		System.out.println("字符串是否是Object类的实例:"+(a instanceof Object));
		
		TestInstanceof ti1 = new TestInstanceof();
		System.out.println("ti1 对象是否是Object类的实例 ;"+(ti1 instanceof Object));
		TestInstanceof2 ti2 = new TestInstanceof2();
		//System.out.println("ti2 对象是否是Object类的实例 ;"+(ti2 instanceof ti1));  //第二个操作数是对象,编译出错
		System.out.println("ti2 对象是否是Object类的实例 ;"+(ti2 instanceof TestInstanceof2));
		
		//前后类型不同编译出错。
		//System.out.println("a 是否是TestInstanceof2类的实例 ;"+(a instanceof TestInstanceof2));
	}
}
class TestInstanceof {     }

class TestInstanceof2{     }


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值