对象
一、类与对象
类与对象概念:
- 类:类是抽象的,概念的,是一类事物的特征的集合体,代表的是一类事物;类是一种数据类型
- 对象:对象是具体的,是实际的,代表的是某一类事物中具体的个体,即实例。
- 类是对象的模板,对象是类的一个个体。
创建对象
-
先声明再创建:
Cat cat; //声明 cat = new Cat(); //创建
-
直接创建
Cat cat = new Cat();
对象属性的访问:
对象名.属性名;
对象的内存分配机制
- 实例化对象才会开辟对象空间
- new一个对象,就是在实例化一个对象;
package com.muyu.javase.object;
public class ObjectDemo1 {
public static void main(String[] args) {
Cat cat = new Cat();//实例化对象
cat.name = "小橘"; //初始化对象属性
cat.age = 2;
cat.weight = 2.5;
System.out.println(cat.name);
}
}
//定义猫类
class Cat{
//声明类属性
String name;
int age;
double weight;
}
Java内存的结构分析:
- 栈:一般存放基本数据类型(局部变量);
- 堆:存放对象、数组等;
- 方法区:常量池(常量,字符串),类加载信息;
对象内存分配机制:
-
实例化对象后,先在方法区加载类的属性和方法信息;
-
在堆区依据类的信息,开辟对象空间(对象属性未初始化时,使用的是数据的默认值),并依据属性数量在对象空间内部开辟属性空间存储属性值
-
初始化对象属性:如果属性是基本数据类型,则直接存在在对象空间内部的属性空间内;
如果是引用数据类型,则属性空间存储的是引用数据的空间地址,String数据类型的空间地址在方法区的常量池中。
(1)实例化对象;(2)加载类信息;(3)开辟对象空间;(4)(5)(6)初始化对象属性;
二、类属性/成员变量:
- 属性也叫成员变量,是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象,数组)
//定义猫类
class Cat{
//类属性
String name;
int age;
double weight;
}
- 属性声明:
访问修饰符 数据类型 属性名;//属性声明:
访问修饰符(public, protected, private.默认default)
- 属性如果不赋值,使用的是对应数据类型的默认值。
三、成员方法:
1、方法定义:
- 有返回值
修饰符 返回数据类型 方法名(形参列表){
方法体;
return 返回值;
}
- 无返回值
访问修饰符 void 方法名(形参列表){
方法体;
}
使用成员方法可以提高代码的复用性
2、方法注意事项和使用细节
-
方法定义注意事项
1访问修饰符(作用是控制方法使用范围:public, protected, private.默认default
2、如果方法要求有返回数据类,则方法体中必须有(return 值;)语句,而且返回值类型必须和return的值类型一致或者兼容。
3、一个方法最多有一个返回值,如有多个可以返回数组;
4、如果是void 修饰的方法,可以没有return语句,或者只写return;
5、方法命名遵循驼峰命名,见名知义。
6、方法内部不能嵌套方法。
-
形参列表注意事项
- 一个方法可以有0个或者多个参数,中间用逗号隔开;
- 调用带有参数的方法时,要对应着形参列表传入相同数据类型或者兼容的实参
- 方法定义是的参数是形式参数,方法调用时传入的参数是实际参数。
- 参数可以是任意数据类型,包含基本类型和引用类型。
-
方法调用细节
- 同一个类中的方法调用,直接使用方法名() 调用。有参数就传入参数
- 跨类的方法调用,需要先实例化对象,再通过对象.方法名(),调用。有参数就传入参数
- 跨类的方法调用与访问修饰符有关。
package com.muyu.javase.object;
public class ObjectDemo2 {
public static void main(String[] args) {
//创建对象
People ultraman = new People();
ultraman.height = 54;
ultraman.age = 18;
ultraman.name = "迪迦";
System.out.println("我叫"+ultraman.name+";"+"我"+ultraman.age+"岁了;"+"我是一名"+People.race);//我叫迪迦;我18岁了;我是一名奥特曼
ultraman.speek();//我是奥特曼;我要打小怪兽
ultraman.calculate(100);//我会计算1-100的和:5050
int sum = ultraman.getSum(5,26);//有返回值需要一个变量进行接收
System.out.println(sum);//31
}
}
class People{
static String race = "奥特曼"; // 类变量,这个类的属性,可以通过对象.属性名/类名.属性名获取;可以在类的内部使用类名.属性名调用
int age ; //对象变量,只能通过对象.属性名获取; 不能在类的内部调用
int height;
String name;
public void speek(){
String name = People.race; // 调用类属性
System.out.println("我是"+name+";"+"我要打小怪兽");
}
void calculate(int n){
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
System.out.println("我会计算1-"+n+"的和:"+sum);
}
int getSum(int a, int b){
int sum = a + b;
return sum;
}
int getSum100(){
int a = 41;
int b = 59;
int sum = getSum(a,b); //直接调用getSum方法
return sum;
}
}
3、方法调用机制
- 创建对象
- 方法区加载类信息(属性,方法)
- 开辟对象空间;
- 调用方法,在栈区开辟临时的方法执行的独立空间,方法执行完毕,空间销毁;
- 返回主方法继续执行后面的程序语句
4、成员方法传参机制(重点)
- 基本数据类型的传参
package com.muyu.javase.object;
public class ObjectDemo3 {
public static void main(String[] args) {
int a = 10;
int b = 20;
Calcurlate cal = new Calcurlate();
cal.getSum(a,b); // 120;a:100
System.out.println(a+b+";"+"a:"+a); //30;a:10
}
}
class Calcurlate{
public void getSum(int a,int b){
a = 100;
int sum = a + b;
System.out.println(sum+";"+"a:"+a);
}
}
1、实例化对象;
2、加载类信息;
3、返回对象空间地址(cal是对象空间地址的引用);
4、调用对象方法,在栈区开辟临时执行方法的独立空间;
5、对象方法执行完毕,返回执行main方法后面语句,独立空间销毁,变量也被回收。
基本数据类型的传参是 值传递(只拷贝),方法内数据的修改,不会对原数据产生影响
- 引用数据类型的传递
package com.muyu.javase.object;
public class ObjectDemo4 {
public static void main(String[] args) {
int[] a = {1,2,3,4,5};
Array cal = new Array();
System.out.println("cal对象方法执行");
cal.alterArray(a); //10 2 3 4 5
System.out.println("\n"+"main方法执行");
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+ " "); //10 2 3 4 5
}
}
}
class Array{
public void alterArray(int[] a){
a[0] = 10;
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
}
1、创建数组空间,并返回数组空间地址(a是数组空间地址的引用)
2、创建对象,加载类信息;
3、开辟对象空间
4、返回对象空间地址(cal是对象空间地址的引用)
5、调用对象方法,在栈区开辟临时执行方法的独立空间;
6、此时传递的参数是数组空间的地址,指向的0x1001这个数组,通过索引修改数组的元素。
7、方法执行完毕,销毁独立空间,返回main方法继续执行后续语句,此时main方法中的a指向的数组已经被修改。
package com.muyu.javase.object;
public class ObjectDemo5 {
public static void main(String[] args) {
int[] a = {1,2,3,4,5};
Array2 cal = new Array2();
System.out.println("cal对象方法执行");
cal.alterArray2(a); //10 0 0 0 0
System.out.println("\n"+"main方法执行");
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+ " "); //1 2 3 4 5
}
}
}
class Array2{
public void alterArray2(int[] a){
a = new int[5]; //a重新指向了一个新的数组空间,与传入的参数数组断开联系
a[0] = 10;
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
}
当形参是引用类型时,成员方法传参数时,实际传递的是引用类型的空间地址的引用。通俗来讲,就是传入的引用类型的一个名称代号,如果在方法内部不将这个名称代号指向另外一个相同的引用类型或者指向空,那么在方法内,这个名称代号就是表示那一个数组,对其做的任何修改,都会影响原数据。
一个引用类型的空间地址可以有多个变量进行引用,但一个变量只能引用一个空间地址,如果要引用别的空间地址,就需要断开与原来空间地址的引用。
5、方法的递归调用
方法递归调用就是方法自己调用自己,每次调用时传入不同的变量。
方法递归调用的规则
- 执行一个方法时,创建一个新的受保护的独立空间(栈空间);
- 方法的局部变量是独立的,不会相互影响;
- 如果方法中使用的是引用类型变量(如数组,对象),就会共享该引用类型的数据。
- 递归必须向退出递归的条件逼近,否则就是无限递归,出现栈溢出的异常;
- 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁。
package com.muyu.javase.object;
public class RecursionDemo2 {
//猴子吃桃:有一堆桃子,猴子每天吃掉一半,并多出一颗,当第十天想吃的时候只有一颗桃子了,求最初桃子数量
/*
思路:
1、第10天1颗桃子,那第9天的桃子数 = 2 * (1 + 1),第八天的桃子数 = 2 * (第九天没吃之前的桃子数 + 1)
2、由此可以推出 :第n天的桃子数 = (第n+1天的桃子数 + 1)*2
3、关系方程是:f(n) = (f(n+1) + 1)*2
4、要求出第1天的桃子数,就要计算出第2天的桃子数,要求第二天的就要求出第三天的,以此类推,可从第十天计算出第九天的
*/
public static void main(String[] args) {
Peach peach = new Peach();
int nums = peach.oneDayPeach(1);
System.out.println(nums);
}
}
class Peach {
public int oneDayPeach(int n){
int sum = 0;
if(n == 10){
sum = 1;
}else {
sum = (oneDayPeach(n+1)+1)*2;
}
return sum;
}
}
6、方法的重载
java中允许同一个类中,存在多个名字相同的方法,但要求这些方法的形参列表不能相同。方法重载减轻了起名的麻烦,也减轻了记方法名的麻烦。
方法重载的注意事项和使用细节。
- 方法名必须相同
- 形参列表必须不同:
- 数据类型的不同
- 参数数量的不同
- 数据类型(参数)传入的顺序不同
- 返回值无要求
package com.muyu.javase.object;
public class MethodOverload {
//方法重载
public static void main(String[] args) {
int[] a = {1,2,3,};
Calculate calculate = new Calculate();
System.out.println("NumSum:"+calculate.NumSum(4,6)); //>>>10
System.out.println("NumSum:"+calculate.NumSum(4.5,6)); //>>>10.5
System.out.println("NumSum:"+calculate.NumSum('a',6)); //>>>g
System.out.println("NumSum:"+calculate.NumSum(a)); //>>>6
}
}
class Calculate{
public int NumSum(int a, int b){
return a + b;
}
public double NumSum(double a, int b){
return a + b;
}
public char NumSum(char a, int b){
return (char)(a + b);
}
public int NumSum(int[] a){
int sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
}
return sum;
}
}
7、可变参数的使用
java允许将同一个类中多个 同名 同功能 但参数 个数 不同的方法,封装成一个方法。
基本语法:
访问修饰符 返回类型 方法名(数据类型...形参名){
//语句块
}可变参数**注意事项和使用细节
package com.muyu.javase.object;
public class VariableParameter {
//可变参数
public static void main(String[] args) {
HspMethod hspMethod = new HspMethod();
System.out.println(hspMethod.sum(1,5,8,9,7)); //>>> 30
}
}
class HspMethod{
public int sum(int...args){
int sum = 0;
//可变参数本质是一个数组。
for (int i = 0; i < args.length; i++) {
sum += args[i];
}
return sum;
}
}
可变参数注意事项和使用细节
- 可变参数的实参个数可以是0个或者多个;
- 可变参数的实参可以是数组
- 可变参数的本质是数组。
8、变量作用域(scope)
在java编程中,主要的变量时属性(成员变量)和局部变量。
全局变量
定义在方法之外的变量,作用域是整个类。在类中可以直接被调用。
全局变量伴随着对象创建而创建,伴随着对象结束而销毁。
局部变量
- 局部变量声明在方法、构造方法或者语句块中;
- 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;
- 访问修饰符不能用于局部变量;
- 局部变量只在声明它的方法、构造方法或者语句块中可见;
- 局部变量是在栈上分配的。
- 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
作用域的注意事项和使用细节
- 全局变量和局部变量可以重名,访问时遵循就近原则。
- 在同一个作用域中,两个变量不能同名;
- 全局变量生命周期长,伴随着对象创建而创建,伴随着对象结束而销毁。局部变量生命周期短,在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,局部变量将会被销毁;
- 作用域范围不同,全局变量可以被本类或其他类使用(对象调用);局部变量只能在本类对应的方法或者语句块中使用。
- 修饰符,全局变量可以使用修饰符,局部变量不能使用修饰符。
- 全局变量有默认值,局部变量没有默认值,使用局部变量必须在初始化赋值以后。
package com.muyu.javase.object;
public class VariableScope {
//变量作用域
public static void main(String[] args) {
VarScope varScope = new VarScope();
varScope.information(); // Jack今年14岁了(就近取age)
}
}
class VarScope{
String name = "Jack";
int age = 11;
public void information(){
int age = 14;
System.out.println(name + "今年"+age+"岁了");
}
}
9、构造方法/构造器
构造方法又叫构造器(Constructor),是类的一种特殊方法,主要作用是对新对象的初始化(对应python中 init 方法):
java中的构造方法有如下特点:
- 方法名必须和类名相同
- 没用返回类型(即没有void和返回数据类型)
- 在创建对象的时候,系统自动调用,进行对象的初始化
基本语法:
[修饰符] 方法名(形参列表){
//方法体
}
package com.muyu.javase.object;
public class Constructor {
//构造器/构造方法
public static void main(String[] args) {
ConstructorDemo constructorDemo = new ConstructorDemo("jack",14);
//>>>构造方法被调用(创建对象是被系统调用)
}
}
class ConstructorDemo{
String name;
int age;
public ConstructorDemo(String aName,int bAge){
name = aName;
age = bAge;
System.out.println("构造方法被调用");
}
}
构造方法/构造器的注意事项和使用细节
- 一个类就可以定义多个不同的构造器,即构造器重载;
- 构造器的方法名必须和类名相同
- 构造器没用返回类型(即没有void和返回数据类型)
- 构造器是完成对象的初始化,而不是创建对象;
- 在创建对象时,由系统自动调用进行对象的初始化。;
- 如果类中没用定义构造器,系统会自动给类生成一个默认无参数的构造器(也叫默认构造方法),可以使用javap进行反编译*.class文件查看。
- 一旦定义了自己的构造器,默认构造器就被覆盖,无法使用,除非显示的定义一下。
package com.muyu.javase.object;
public class Constructor {
//构造器/构造方法
public static void main(String[] args) {
ConstructorDemo constructorDemo1 = new ConstructorDemo("jack",14);
//>>>自定义构造方法被调用(创建对象是被系统调用)
ConstructorDemo constructorDemo2 = new ConstructorDemo();
//>>>默认构造方法被调用
}
}
class ConstructorDemo{
String name;
int age;
public ConstructorDemo(String aName,int bAge){
name = aName;
age = bAge;
System.out.println("自定义构造方法被调用");
}
ConstructorDemo(){ // 默认构造器
System.out.println("默认构造方法被调用");
}
}