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类第一次初始化时,系统会为该类的类变量分配内存空间,并指定默认值。
局部变量的初始化和内存中的运行机制
局部变量定义后,必须经过显式初始化后才能使用,系统不会为局部变量执行初始化,这意味着定义局部变量后,系统并未为这个变量分配内存空间,直到等到程序为这个变量赋初始值时,系统才会局部变量分配内存,并将初始化值保存到这块内存中。与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中的。如果局部变量 是基本类型的变量,则直接把这个变量的值保存在该变量对应内存中;如果局部变量是一个引用类型的变量,则这个变量里存放的是地址,通过该地址引用到该变量实际引用的对象或数组。
栈内存中的变量无须系统垃圾回收,栈内存中的变量往往是随方法或代码块的运行结束而结束的。因此,局部变量的作用域是从初始化该变量开始,直到该方法或该代码块运行完成而结束。因为局部变量只保存基本类型的值或者对象的引用,因此局部变量所占的内存区通常比较小。
相关文章推荐
- Java基础-07.总结private,this,封装,static,成员方法变量,局部变量匿名对象
- java基础七(成员变量 局部变量 匿名对象 封装 设计原则 private this 构造方法 static)
- java 面向对象基础 类 成员变量和局部变量 成员方法
- java中对象、成员变量、静态变量、方法的内存分配
- Adnroid ndk 中jni访问java方法、对象、成员变量
- JAVA之旅(四)——面向对象思想,成员/局部变量,匿名对象,封装 , private,构造方法,构造代码块
- JAVA之旅(四)——面向对象思想,成员/局部变量,匿名对象,封装 , private,构造方法,构造代码块
- Java基础05:面向对象;类与对象;匿名对象;成员变量;局部变量;封装;构造函数
- 7 面向对象(成员变量和局部变量的区别,类作为形式参数的问题,匿名对象,封装,private关键字,this关键字,构造方法,类的初始化过程,static关键字)
- 【JavaSE学习笔记】面向对象_01(入门,匿名对象,成员变量,局部变量,封装,this,构造方法)
- java高级类和对象、方法、构造方法及成员局部变量
- Java语言基础{Java_se(05)}-面向对象-封装-构造方法-成员变量,局部变量
- Java基础知识_day07_(面向对象,构造方法,封装,成员变量与局部变量)
- Java反射---Class对象,获得类的方法、成员变量和构造函数信息
- JAVA面向对象基础:成员变量、方法、方法的重载、构造函数
- java 基础之成员变量,成员方法和局部变量,实例方法
- JAVA基础day05 面向对象思想 成员变量和方法 封装 构造方法和this的使用
- java个人学习笔记06(类、对象、成员变量、局部变量)
- 【Java】对象、类(class)、变量(成员变量field、局部变量)、包(package)
- JAVA基础再回首(四)——面向对象思想、类与对象、成员/局部变量、匿名对象、封装、private、this、构造方法