成员变量的分类
- 实例变量:没有static修饰,也叫对象属性,属于某个对象的,通过对象来使用
- 类变量:有static修饰,也叫类变量,属于整个类的,不是属于某个实例、
如何声明成员变量
成员变量的类型可以是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修饰
- 静态变量被static修饰
- 实例变量不能被static修饰
内存中的分数不同
- 类变量:内存中只有一份
- 实例变量:每创建一份都会开辟一块独立的空间
调用方式不同
- 类变量: 可以使用对象名.属性名和类名.属性名调用,推荐使用类名.属性名调用调用
- 实例变量:对象名.属性名
生命周期不同:
- 类变量:随着类的加载而加载 随着类的消亡而消亡
- 实例变量:随着对象的创建 对象消失(当对象没有引用指向时) 被垃圾回收器回收
存储的位置不一样
- 类变量:方法区
- 实例变量:堆中
成员变量和局部变量的区别
查看下面代码
public class Car {
String color;// 成员变量
// 成员方法
public void drive(String name) {
//声明在方法上的name和定义在方法里的speed都是局部变量
int speed = 80;
System.out.println("汽车正在以" + speed + "迈的速度行驶...");
}
}
成员变量和局部变量的主要区别如下:
定义的位置不同:
- 成员变量定义在类中方法外,
- 局部变量定义在方法中或者是方法声明上
在内存中的位置不同:
- 成员变量是在堆区,
- 局部变量是在栈区
生命周期不同:
- 成员变量是随着对象的创建而存在,随着对象的销毁而销毁
- 局部变量是随着方法的调用而存在,随着方法调用完毕而销毁
默认值不同:
- 成员变量有默认值
- 局部变量没有默认值,不赋值不能直接使用
成员变量是用来存储对象的数据信息的,那么如何表示对象的行为功能呢?就要通过方法来实现
概念:
方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。把一个功能封装为方法的目的是,可以实现代码重用,从而简少代码量。
方法的使用原则
- 必须先声明后使用。类,变量,方法等都要先声明后使用
- 不调用不执行,调用一次执行一次。谁先调用,谁先执行
- 方法执行完毕之后,会回到方法调用处
成员方法分为两类
- 实例方法:没有static修饰的方法,必须通过实例对象来调用。
//有参,有返回值的实例方法
public int getMax(int a, int b) {
return (a > b ? a : b);
}
- 静态方法:有static修饰的方法,也叫类方法,可以由实例对象来调用也可以由类名来调用。推荐使用类名调用
//有参,有返回值的静态方法
public static int getMax(int a, int b) {
return (a > b ? a : b);
}
声明方法语法格式
格式详解:
- 修饰符: 修饰符后面一一介绍,例如:public,static等都是修饰符
- 返回值类型:表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。可以是基本数据类型也可以是引用数据类型,如果无返回值类型就可以使用void来代替
- 方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字
- 参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型。带参方法定义时,参数中的数据类型与变量名都不能缺少,缺少任意一个程序将报错。带参方法定义时,多个参数之间使用逗号(,)分隔。参数列表也可以为空。
- 方法体:方法要完成特定功能代码
- return:结束方法,并将方法的结果返回去。如果返回值类型不是void,方法体中必须保证一定有return 返回值;语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。如果返回值类型为void时,return 后面不用跟返回值,甚至也可以没有return语句。return语句后面就不能再写其他代码了,否则会报错:Unreachable code
如何在其他类中调用方法
实例方法
类方法
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(); //推荐
}
}
形参和实参
- 形参:在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
- 实参:调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。
总结:
- 调用时,需要传“实参”,实参的个数、类型、顺序顺序要与形参列表一一对应如果方法没有形参,就不需要也不能传实参。
- 调用时,如果方法有返回值,可以接受或处理返回值结果,当然也可以不接收,那么此时返回值就丢失了。如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。
在本类中访问本类的成员变量和成员方法
- 直接写成员变量名或者是成员方法名,不需要加“对象名."和"类名."。唯一例外:静态方法中不能直接访问本类的非静态的成员变量和成员方法
public class Demo02Method {
//无参,无返回值实例方法
public void method1() {
method2();//本类中调用其他方法
System.out.println("无参,无返回值方法");
}
//无参,有返回值静态方法
public static int method2() {
//method1();错误:静态方法中不能直接访问本类的非静态的成员变量和成员方法
return 1;
}
}
方法调用内存分析
方法不调用不执行,调用一次执行一次,每次调用会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值,当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。
查看下列代码,分析是如何执行的
package demo01;
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中的数据,返还给方法的调用处/者
}
}
图解分析
方法的调用总结:
有返回值的方法调用方式
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;
}
}
无返回值的方法调用方式
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 ;//结束方法,返回到方法的调用处,注意没有带回任何数据
}
}
方法的注意事项
- 方法不能嵌套定义(在定义方法的内部又定义了其它方法),可以调用其它方法
- 方法可以嵌套调用
- 返回值类型,必须要和 return 语句返回的数据的类型要匹配,否则编译失败 。
- 不能在 return 后面写代码, return 意味着方法结束,所有后面的代码永远不会执行,属于无效代码。
- void表示无返回值,可以省略return,也可以单独的书写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){
}
}
总结: 同类多个方法同名的前提下,。 只看多个方法的参数(除了名称以外)有区别,就构成重载
方法参数传递前置知识:
- 使用=进行赋值的特点:把基本类型变量a的值赋值给基本类型变量b时,其实是把a中的值复制一份给变量b,之后不管如何修改变量b中的值,都不会影响变量a中的值
- 变量的作用范围:方法内部定义的变量只在所定义的方法内有效(可以使用),出了方法的作用范围,就不能使用了。
基本数据类型作为方法参数
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
}
}
图解:
结论:
- 基本数据类型的参数,形式参数的改变,不影响实际参数
结论依据:
- 每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失
引用数据类型作为方法参数
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
}
}
图解
结论:
- 对于引用类型的参数,形式参数的改变,影响实际参数的值
结论依据:
- 引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法弹栈,堆内存中的数据也已经是改变后的结果



