成员变量的分类
如何声明成员变量
成员变量的类型可以是Java的任意类型,包括基本数据类型、引用数据类型(类、接口、数组等 例如:声明一个中国人的类
class Chinese{
static String country;//类变量
String name;//默认值
char gender = '男';//显式赋值
}
如何在类外面访问成员变量
类变量
成员变量/实例变量
代码示例
class Chinese {
static String country;
String name;
char gender = '男';
}
public class Demo {
public static void main(String[] args) {
//System.out.println(Chinese.name);错误,非静态成员变量必须通过对象.进行访问
//创建对象
Chinese c1 = new Chinese();
//对象名.非静态成员变量
System.out.println(c1.name);//null
//对象名.非静态成员变量
System.out.println(c1.gender);//男
//类名.静态成员变量,推荐
System.out.println(Chinese.country);//null
//静态的成员变量也可以通过对象.进行访问,不推荐
System.out.println(c1.country);//null
}
}
成员变量有默认值
类变量的值是所有对象共享的,当类变量发生改变的时候,任何此类的对象访问该变量都会发生改变。而实例变量的值是每个对象独立的。
class Chinese {
static String country;
String name;
char gender = '男';
}
public class Demo {
public static void main(String[] args) {
//创建对象c1
Chinese c1 = new Chinese();
//创建对象c2
Chinese c2 = new Chinese();
//给对象c1的name属性赋值张三
c1.name = "张三";
//给对象c2的name属性赋值李四
c2.name = "李四";
//给对象c2的gender属性赋值女
c2.gender = '女';
//给类变量赋值
Chinese.country = "中国";//推荐
//访问各个对象的成员变量
System.out.println("c1.country = " + c1.country); //中国
System.out.println("c1.name = " + c1.name); //张三
System.out.println("c1.gender = " + c1.gender);//男
System.out.println("c2.country = " + c2.country); //中国
System.out.println("c2.name = " + c2.name);//李四
System.out.println("c2.gender = " + c2.gender);//女
}
}
成员变量的内存图
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来。我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。Java虚拟机要运行程序,必须要对内存进行空间的分配和管理,每一片区域都有特定的处理数据方式和内存管理方式。
详解:
下面我们使用画图的方式来分析一下为什么,类变量的值是当前类创建的所有对象共享的,而实例变量的值是每个对象独立
静态成员变量和实例变量的异同
相同点:
不同点:
有无static修饰
内存中的分数不同
调用方式不同
生命周期不同:
存储的位置不一样
成员变量和局部变量的区别
查看下面代码
public class Car {
String color;// 成员变量
// 成员方法
public void drive(String name) {
//声明在方法上的name和定义在方法里的speed都是局部变量
int speed = 80;
System.out.println("汽车正在以" + speed + "迈的速度行驶...");
}
}
成员变量和局部变量的主要区别如下:
定义的位置不同:
在内存中的位置不同:
生命周期不同:
默认值不同:
成员变量初始化方式
实例初始化
实例初始化的目的:为类中非静态成员变量赋值
实际上我们编写的代码在编译时,会自动处理代码,整理出一个
实例初始化方法的方法体,由四部分构成:
特别说明:其中2和3是按顺序合并的,1一定在最前面4一定在最后面
执行特点
类的初始化
类初始化的目的:为类中的静态变量进行赋值。
实际上,类初始化的过程时在调用一个
结论:
每一个类都有一个类初始化方法
类初始化与实例初始化
成员变量是用来存储对象的数据信息的,那么如何表示对象的行为功能呢?就要通过方法来实现
概念:
方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。把一个功能封装为方法的目的是,可以实现代码重用,从而简少代码量。
方法的使用原则
成员方法分为两类
//有参,有返回值的实例方法
public int getMax(int a, int b) {
return (a > b ? a : b);
}
//有参,有返回值的静态方法
public static int getMax(int a, int b) {
return (a > b ? a : b);
}
声明方法语法格式
格式详解:
如何在其他类中调用方法
实例方法
类方法
public class Demo02Method {
//无参,无返回值实例方法
public void method1() {
System.out.println("无参,无返回值方法");
}
//无参,有返回值静态方法
public static int method2() {
return 1;
}
}
class TestDemo02Method{
public static void main(String[] args) {
//创建对象
Demo02Method method = new Demo02Method();
//调用实例方法
method.method1();
//调用静态方法
method.method2(); //不推荐
Demo02Method.method2(); //推荐
}
}
形参和实参
总结:
在本类中访问本类的成员变量和成员方法
public class Demo02Method {
//无参,无返回值实例方法
public void method1() {
method2();//本类中调用其他方法
System.out.println("无参,无返回值方法");
}
//无参,有返回值静态方法
public static int method2() {
//method1();错误:静态方法中不能直接访问本类的非静态的成员变量和成员方法
return 1;
}
}
java 程序开始执行的时候先通过类加载器子系统找到硬盘上的字节码 (class)文件,然后将其加载到 java 虚拟机的方法区当中,开始调用 main 方法,main 方法被调用的瞬间,会给 main 方法在“栈”内存中分配所属的活动空间,此时发生压栈动作,main 方 法的活动空间处于栈底。
也就是说,方法只定义不去调用的话,只是把它的代码片段存储在方法区当中,java 虚拟机是不会在栈内存当中给该方法分配活动空间的,只有在调用的瞬间,java 虚拟机才会在“栈 内存”当中给该方法分配活动空间,此时发生压栈动作,直到这个方法执行结束的时候,这个 方法在栈内存中所对应的活动空间就会释放掉,此时发生弹栈动作。
由于栈的特点是先进后出, 所以最先调用的方法(最先压栈)一定是最后结束的(最后弹栈)。比如:main 方法最先被 调用,那么它一定是最后一个结束的。换句话说:main 方法结束了,程序也就结束了(目前 来说是这样)。
方法不调用不执行,调用一次执行一次,每次调用会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值,当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。
方法体当中的代码是有执行顺序的,必须遵循自上而下的顺序依次逐行执行,当前行代码必须执行结束之后,下一行代码才能执行,不能跳行执 行,还记得吗?现在再和栈数据结构一起联系起来思考一下,为什么要采用栈数据结构呢?是 不是只有采用这种先进后出的栈数据结构才可以保证代码的执行顺序呢!
查看下列代码,分析是如何执行的
package demo01;
/*
1.练习:
设计一个方法可以获取两个int数的较大值,数据来自于参数
2.三要素:
(1)方法名称: getMax
(2)参数列表: int a,int b
(3)返回值类型: int
*/
public class Demo03GetMax {
public static void main(String[] args) {
System.out.println("main...start...");
//调用方法: 传递的是常量
int result = getMax(100,200);
System.out.println("100和200的最大值: "+result);
//调用方法方法: 传递变量
int a = 10, b = 20;
int max = getMax(a,b);
System.out.println(a+"和"+b+"的最大值: "+max);
System.out.println("main...end...");
}
//设计一个方法可以获取两个int数的较大值,数据来自于参数
public static int getMax(int a, int b) {
int max = (a>b) ? a : b;
return max;//结束方法,并且把max中的数据,返还给方法的调用处/者
}
}
图解分析
方法的调用总结:
有返回值的方法调用方式
/*
有返回值的方法调用方式
1.赋值调用: 把有返回值的方法的调用结果保存到对应的变量中 ----推荐使用----
数据类型 变量名称 = 方法名称(参数...);
2.输出/打印调用: 把有返回值的方法的调用结果直接交给输出语句
System.out.println(方法名称(参数...));
3.单独调用: 既不保存方法的结果,也没有对结果进行输出 -----不推荐使用,没有意义-----
方法名称(参数...);
*/
public class Demo01DYMethod {
public static void main(String[] args) {
System.out.println("main...start...");
//1.赋值调用: 把有返回值的方法的调用结果保存到对应的变量中
//数据类型 变量名称 = 方法名称(参数...);
int result = getSum(10,20);
//可以对结果数据做其它操作
//result *= 100;
System.out.println("和: "+result);
//2.输出/打印调用: 把有返回值的方法的调用结果直接交给输出语句
//System.out.println(方法名称(参数...));
System.out.println(getSum(100,200));
//3.单独调用: 既不保存方法的结果,也没有对结果进行输出
getSum(5,10);
System.out.println("main...end...");
}
//定义方法,获取2个int数字之和
public static int getSum(int a, int b) {
int sum = a + b;
return sum;
}
}
无返回值的方法调用方式
/*
无返回值的方法调用方式
1.单独调用: 既不保存方法的结果,也没有对结果进行输出 只能采用单独调用
方法名称(参数...);
2.赋值调用: 把有返回值的方法的调用结果保存到对应的变量中 不能赋值调用
数据类型 变量名称 = 方法名称(参数...);
3.输出/打印调用: 把有返回值的方法的调用结果直接交给输出语句 不能输出调用
System.out.println(方法名称(参数...));
*/
public class Demo02DYMethod {
public static void main(String[] args) {
System.out.println("main...start...");
//1.单独调用: 既不保存方法的结果,也没有对结果进行输出
//方法名称(参数...);
printSum(10,20);
//2.赋值调用: 把有返回值的方法的调用结果保存到对应的变量中
//数据类型 变量名称 = 方法名称(参数...);
//int a = printSum(5,15);//错误的,int变量只能保存整数,但是printSum方法执行结束没有返回任何结果数据
//void a = printSum(5,15);//错误的,void根本不是数据类型
//3.输出/打印调用: 把有返回值的方法的调用结果直接交给输出语句
//System.out.println(方法名称(参数...));
//System.out.println(printSum(3,2));//错误: 因为printSum方法执行完毕后,没有任何结果返回
System.out.println("main...end...");
}
//定义方法,打印2个int数字之和
public static void printSum(int a, int b) {
int sum = a + b;
System.out.println("和: "+sum);
return ;//结束方法,返回到方法的调用处,注意没有带回任何数据
}
}
方法的注意事项
概念:
作用/目的:
JVM 调用
方法重载与哪些因素无关
方法重载中参数列表不同有哪些情况
代码示例
public class Demo {
public static void main(String[] args) {
method(10,10.0);
}
//1.此方法只有一个int类型参数
public static void method(int a) {
}
//2.此方法只有两个int类型参数
//方法2和方法1参数的数量是不同的,可以构成重载
public static void method(int a,int b) {
}
//3.此方法只有一个double类型参数
//方法3和方法2参数的数量是不同的,可以构成重载
//方法3和方法1参数虽然都是只有一个,但是类型不同,可以构成重载
public static void method(double a) {
}
//4.此方法有一个int类型参数和一个double类型参数
public static void method(int a,double b){
}
//5.此方法有一个double类型参数和一个int类型参数
//方法5和方法4,虽然参数都是2个,但是类型的顺序不同
public static void method(double a,int b){
}
}
总结: 同类多个方法同名的前提下,。 只看多个方法的参数(除了名称以外)有区别,就构成重载
前置知识:
基本数据类型作为方法参数
/*
注意:
1.基本类型变量: 保存的是具体的数据值
2.基本类型变量作为形式参数,形式参数的改变,不会影响实际参数
基本类型变量作为形式参数:
定义方法时,()中定义的参数属于基本类型
不会影响实际参数: 调用方法时,()中给出的参数
*/
public class Demo {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("ms...a="+a);//10
System.out.println("ms...b="+b);//20
//调用方法
change( a , b );
System.out.println("me...a="+a);//10
System.out.println("me...b="+b);//20
}
public static void change(int a, int b) {
System.out.println("cs...a="+a);//10
System.out.println("cs...b="+b);//20
a = a*10;
b = b*10;
System.out.println("ce...a="+a);//100
System.out.println("ce...b="+b);//200
}
}
图解:
结论:
结论依据:
引用数据类型作为方法参数
/*
1.引用类型变量: 保存的是对象在堆内存空间的地址值,进行参数传递的时候,传递的也是地址值
2.引用类型变量作为形式参数,通过形式参数找到对应的堆内存空间,修改堆内存空间的内容之后,
通过实际参数看到的一定是修改后的堆内存空间的内容
引用类型作为形式参数,形式参数的改变,会影响实际参数
数组:
1.数组也是一种引用类型: 数组名称保存的也是数组在堆内存空间的地址值
2.数组作为方法参数或者返回值: 传递的都是数组在堆内存空间的地址值
*/
public class Demo03RefVar {
public static void main(String[] args) {
int[] arr = { 10 , 20 };
//System.out.println(arr);//数组名称: 保存数组在内存中的地址值[I@1540e19d
System.out.println("ms...arr[0]="+arr[0]);//10
System.out.println("ms...arr[1]="+arr[1]);//20
//调用方法
change( arr );
System.out.println("me...arr[0]="+arr[0]);//100
System.out.println("me...arr[1]="+arr[1]);//200
}
public static void change(int[] arr ) {
System.out.println("cs...arr[0]="+arr[0]);//10
System.out.println("cs...arr[1]="+arr[1]);//20
arr[0] = arr[0]*10;
arr[1] = arr[1]*10;
System.out.println("ce...arr[0]="+arr[0]);//100
System.out.println("ce...arr[1]="+arr[1]);//200
}
}
图解
结论:
结论依据:
前提:在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化.
其实可变参数本质上就是一个数组
只是后面这种定义,在调用时必须传递数组,而前者更灵活,既可以传递数组,又可以直接传递数组的元素,这样更灵活了。
要求:
/*
编译报错,因为一个方法,只能有一个可变参数
public static void method1(int... nums,String... strs){
}*/
/*
编译报错,因为如果方法有多个参数,可变参数一定要放在末尾
public static void method2(int... nums,String str){
}
*/
代码实例
//求n个整数的和
public class ChangeArgs {
public static void main(String[] args) {
int[] arr = {1, 4, 62, 431, 2};
int sum1 = getSum1(arr);
System.out.println(sum1); //500
int sum2 = getSum2(arr);
System.out.println(sum2); //500
}
// 完成数组 所有元素的求和
// 原始写法
public static int getSum1(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
// 可变参数写法
public static int getSum2(int... arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
}
递归:指在当前方法内调用自己的这种现象。
递归的分类:
注意事项:
代码实例
package demo02;
public class RecursionMethod2 {
public static void main(String[] args) {
int jieCheng = jieCheng(10);
System.out.println("10的阶乘是:" + jieCheng); //3628800
}
//使用递归求阶乘
public static int jieCheng(int n) {
if (n <= 1) {
return 1;
}
return n * jieCheng(n - 1);
}
}