您的位置:首页 > 编程语言 > Java开发

Java类和对象、方法、成员变量和局部变量

2018-01-29 11:47 281 查看

参考资料

[1]. 疯狂Java讲义(第三版) 李刚

类和对象

定义类

类(class)和对象(object,也被称为实例,instanceof),其中类是某一批对象的抽象,可以把类理解成某种观念,对象才是一个具体的实体。

普通类或抽象类的语法如下:

[public] [final] [abstract] class 类名 [extends 被继承的类名,] implements 接口1, 接口2, ...
{
零到一个初始化块或静态初始化块...
零个到多个构造器定义...
零到多个成员变量...
零到多个方法...
}


定义成员变量的语法如下:

[public|protected|private] [static] [final] 类型 成员变量名称 [=默认值];


定义抽象方法的语法如下:

[public|protected|private]   abstract   返回类型   类名( [形参列表] );


定义普通方法的语法如下:

[public|protected|private]  [static] [final] [返回类型|void]    类名( [形参列表] )
{
...
}


定义构造器的语法如下:

[public|protected|private]   构造器名   ( [形参列表] )
{
...
}


对象的产生和使用

声明一个Person类

/**
* 测试类
*/
public class Person {
/**
* 成员变量1
*/
public String name;
/**
* 成员变量2
*/
public int age;

/**
* say方法
* @param content
*/
public void say(String content)
{
System.out.println(content);
}
/**
* 静态talk方法
* @param content
*/
public static void talk(String content)
{
System.out.println(content);
}
}


声明一个类

// 声明一个Person类型的变量
Person p;
// 实例化一个Person类赋值给p
p = new Person();


使用一个类

// 使用静态类
Person.talk("talk");
// 使用非静态类
p = new Person();
p.name = "小a";
p.say("say");


对象、引用和指针

// 使用非静态类
p = new Person();
p.name = "小a";
p.say("say");


p变量被存到栈内存里了,它指向实际的Person对象,真正的Person对象则放在堆(heap)内存中,不管是数组还是对象,都只能通过引用来访问它们,比如变量p,如下图。



多个引用共享一个堆(heap)内存,代码如下

Person p;
p = new Person();
// 引用一个p2变量
Person p2 = p;
p.name = "小a";
// 当输出p2的name属性时可以看到它已经是"小a"
System.out.println(p2.name);


如果希望通知系统回收该对象,只需切断该对象的所有引用变量即可,可以使用null。

Person p;
p = new Person();
// 通知系统回收该对象
p = null;


对象的this引用

this关键字最大的作用就是让类中的一个方法,访问该类的另一个类方法或实例变量。

package com.qunar.c09;

public class Dog {
/**
* name
*/
String name = "dog1";
/**
* age
*/
Integer age = 3;

/**
* 构造器
*/
public Dog()
{
// 设置属性
System.out.println(this.name);
}

/**
* 年龄增长
* @return
*/
public Dog ageAdd()
{
this.age ++;
// 返回一个Dog对象
return this;
}
/**
* jum方法
*/
public void jump()
{
System.out.println("正在执行jump方法");
}

/**
* 定义一个run方法
*/
public void run()
{
// 调用jump方法
this.jump();
// 也可以省略this
// jump();
System.out.println("正在执行run方法");
}
}


当在某个方法中把this作为返回值,则可以连续调用同一个方法

Dog d1 = new Dog();
Dog d2 = d1.ageAdd().ageAdd().ageAdd();
System.out.println(d2.age);


方法详解

方法的所属性

Java语言里方法的所属性主要体现在下面几个方面:

1. 方法不能独立定义,方法只能在类体里定义。

2. 从逻辑意义上来看,方法要么属于该类本身,要么属于该类的一个对象。

3. 永远不能独立执行方法,执行方法必须使用类或对象作为调用者。

方法参数的传递机制

声明方法时包含了形参声明,在调用方法时必须给这些形参指定参数值,调用方法时实际传递给形参的参数值也被称为实参。

Java里方法的参数传递方式只有一种:值传递,即将实际参数值的副本(复制品)传入方法内,而参数本身不会受到影响。

public class PrimitiveTransferTest {

public static  void swap(int a, int b)
{
int tmp = a;
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);
}
}


main()方法定义了a、b两个局部变量。



在swap()方法,系统将main()方法里的a、b局部变量传入,但只是a、b的副本,而不是a、b本身。



在这一步将局部变量a赋值给tem,并交换a和b的引用。



传入对象

/**
* 交换数据对象
*/
public class DataWrap {
int a;
int b;
}


交换过程

public class ReferenceTransferTest {
public static void swap(DataWrap dw)
{
int tmp = dw.a;
dw.a = dw.b;
dw.b = tmp;
System.out.println("swap方法里,a成员变量的值是" + dw.a + ",b成员变量的值是" + dw.b);
}

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);
}
}


程序从main()方法开始执行,main()方法开始创建了一个DataWrap对象,并定义了一个dw对象赋值。



将dw传入swap()方法,dw变量是一个指针,所以系统只复制了DataWrap的引用dw,在swap里面操作的也是dw对象,此时如下图:



如果在swap()方法里面添加代码
dw = null;
以后只是让swap栈区里的dw引用失效,而main栈区里的dw引用不受影响。



形参个数可变的方法

从JDK1.5以后,Java允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。如果在定义方法时,在最后一个形参的类型后增加三个点(…),则表明该形参可以接受多个参数值,多个参数值被当成数组传入。

public class Varargs {
/**
* 测试方法
* @param a
* @param letters
*/
public static void test(int a, String ... letters)
{
// 遍历后面的String形参
for (String letter : letters)
{
System.out.println(letter);
}
System.out.println(a);
}

public static void main(String[] args) {
test(10, "a", "b", "c", "d");
}
}


下面两个方法签名的效果完全一样:

public static void test(int a, String ... letters)
//  调用方法修改为test(10, new String[]{"a", "b", "c", "d"});
public static void test(int a, String[] letters)


数组形式的形参可以处于形参列表的任意位置,但个数可变的形参只能处于形参列表的最后,也就是说,一个方法中最多只能有一个长度可变的形参。

递归方法

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

下面是一个递归方法实现的数列

f(0)=1,f(1)=4,f(n+2)=2∗f(n+1)+f(n)f(0)=1,f(1)=4,f(n+2)=2∗f(n+1)+f(n)

其中nn是大于0 的整数,求f(10)f(10) 的值。

代码如下:

public class Recursive {
public static int fn(int n)
{
if (n == 0)
{
return 1;
}else if(n==1){
return 4;
}else{
return 2 * fn(n - 1) + fn(n - 2);
}
}

public static void main(String[] args) {
// 执行递归
System.out.println(fn(10));
}
}


如果修改题为下面

f(20)=1,f(21)=4,f(n+2)=2∗f(n+1)+f(n)f(20)=1,f(21)=4,f(n+2)=2∗f(n+1)+f(n)

其中nn 是大于0 的整数,求 f(10)f(10) 的值,代码如下:

public static int fn(int n)
{
if (n == 0)
{
return 1;
}else if(n==1){
return 4;
}else{
return  fn(n + 2) -2 * fn(n + 1);
}
}


方法重载

Java允许同一个类里定义多个同名方法,只要形参列表不同就行。如果同一个类中包含了两个或两个以上方法的方法名相同, 但形参不同,则被称为方法的重载。

public class Overload {
/**
* 无参数test
*/
public void test()
{
System.out.println("无参数的test方法");
}

/**
* 有参数的test
* @param msg
*/
public void test(String msg)
{
System.out.println("重载的test方法" + msg);
}

public static void main(String[] args) {
Overload o = new Overload();
o.test();
o.test("Hello");
}
}


成员变量和局部变量

成员变量的分类图如下:



使用示例,代码如下:

public class Person {
public String name;
public static int eyeNum;
}


调用:

// 访问类变量
System.out.println(Person.eyeNum);
// 访问实例变量
Person p = new Person();
System.out.println(p.name);
// 为实例变量赋值
p.name = "孙悟空";
// 为类变量赋值
Person.eyeNum = 2;
System.out.println(p.name+ "," + Person.eyeNum);
Person p2 = new Person();
// 类变量不会随着实例变量变化
System.out.println(p2.eyeNum);


只要离开了代码块局部变量所在的代码块,这个局部变量就立即被销毁,变为不可见。

{
int a =1;
}
// 无法得到a变量
System.out.println(a);


Java允许局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员变量,如果需要在这个方法里引用被覆盖的成员变量,则可使用this(对于实例变量)或类名(对于类变量)作为调用者来限定访问成员变量。

成员变量的初始化和内存中的运行机制

当系统加载类或创建类的实例时,系统自动为成员变量分配内存空间,并在分配内存空间后,自动为成员变量指定初始值。

代码实例

public class Person {
public String name;
public static int eyeNum;
}


调用上面的Person实例

// 创建第一个Person对象
Person p1 = new Person();
// 创建第二个Person对象
Person p2 = new Person();

// 为两个实例变量赋值
p1.name = "孙悟空";
p1.name = "猪八戒";
// 为两个类变量赋值
p1.eyeNum = 2;
p2.eyeNum = 3;


使用演示图说明,如下:

当Person类第一次初始化时,系统会为该类的类变量分配内存空间,并指定默认值。









局部变量的初始化和内存中的运行机制

局部变量定义后,必须经过显式初始化后才能使用,系统不会为局部变量执行初始化,这意味着定义局部变量后,系统并未为这个变量分配内存空间,直到等到程序为这个变量赋初始值时,系统才会局部变量分配内存,并将初始化值保存到这块内存中。

与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中的。如果局部变量 是基本类型的变量,则直接把这个变量的值保存在该变量对应内存中;如果局部变量是一个引用类型的变量,则这个变量里存放的是地址,通过该地址引用到该变量实际引用的对象或数组。

栈内存中的变量无须系统垃圾回收,栈内存中的变量往往是随方法或代码块的运行结束而结束的。因此,局部变量的作用域是从初始化该变量开始,直到该方法或该代码块运行完成而结束。因为局部变量只保存基本类型的值或者对象的引用,因此局部变量所占的内存区通常比较小。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐