文章目录
Java基础
七、面向对象编程(初级)
类与对象
一个程序就是一个世界,有很多事物(对象[属性、行为])
public class Demo01 {
public static void main(String[] args)
{
//使用面向对象解决
//实例化一只猫
//1.new Cat()创建一只猫
//2.Cat cat1 = new Cat();把创建的猫赋给cat1
//cat1就是一个对象
Cat cat1 = new Cat();
cat1.name = "小白";
cat1.age = 3;
cat1.color = "白色";
//创建第2只猫
Cat cat2 = new Cat();
//访问对象的属性
System.out.println("第一只猫的信息:"+cat1.name+" "+cat1.age +" "+cat1.color);
}
}
//使用面向对象
//定义一个猫类
class Cat{
//属性
String name;
int age;
String color;
}
可以看出:
- 类是抽象的,概念的,代表一类事物
- 对象是具体的,实际的,代表一个具体事物
- 类是对象的模板,对象是类的一个个体,对应一个实例
对象在内存中的布局
属性/成员变量
基本介绍:
1)从概念或叫法上看:成员变量 = 属性 = field(字段),即成员变量是用来表示属性的
2)属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象、数组)
注意事项:
1)属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
访问修饰符:控制属性的访问范围
有四种访问修饰符:public,proctected,默认,private
2)属性的定义类型可以为任意类型,包含基本类型或引用类型
3)属性如果不赋值,有默认值,规则和数组一致
//编写一个main 方法
public static void main(String[] args) {
//创建Person 对象
//p1 是对象名(对象引用)
//new Person() 创建的对象空间(数据) 才是真正的对象
Person p1 = new Person();
//对象的属性默认值,遵守数组规则:
//int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
System.out.println("\n 当前这个人的信息");
System.out.println("age=" + p1.age + " name="
+ p1.name + " sal=" + p1.sal + " isPass=" + p1.isPass) ;
}
}
class Person {
//四个属性
int age;
String name;
double sal;
boolean isPass;
}
创建对象
1)先声明再创建
Cat cat; //声明对象cat
cat = new Cat(); //创建
2)直接创建
Cat cat = new Cat();
访问属性:
基本语法:
对象名.属性名;
类和对象的内存分配机制
具体分析:
Java内存结构分析
1)栈:一般存放基本数据类型(局部变量)
2)堆:存放对象(Cat cat,数组等)
3)方法区:常量池(常量,比如字符串),类加载信息
对以下代码创建对象的流程简单分析:
Person p = new Person();
p.name = "jack";
p.age = 10;
1)先加载Person类信息(属性和方法信息,只会加载一次)
2)在堆中分配空间,进行默认初始化(看规则)
3)把地址赋给p,p就指向对象
4)进行指定初始化,比如p.name = “jack” p.age = 10
例子:
Person a = new Person();
a.age = 10;
a.name = "小明";
Person b;
b = a;
System.out.println(b.name); //小明
b.age = 200;
b = null;
System.out.println(a.age); //200
System.out.println(b.age); //异常
成员方法
基本介绍:
在某些情况下,我们需要定义成员方法(简称方法)比如人类:除了有一些属性外( 年龄,姓名…),我们人类还有一些行为比如:可以说话、跑步…,通过学习,还可以做算术题。这时就要用成员方法才能完成。现在要求对Person 类完善。
代码:
public class Demo02 {
public static void main(String[] args) {
//方法使用
//1.方法写好后,如果不去调用,不会输出
//2.先创建一个对象,然后调用其方法即可
Person p1 = new Person();
p1.speak(); //调用方法
p1.cal01(5); //调用cal01方法,同时给n = 5
int ans = p1.getSum(10,20); //调用getSum方法
System.out.println("getSum返回的值是:"+ans);
}
}
class Person{
String name;
int age;
//方法(成员方法)
//添加speak成员方法,输出"我是一个好人"
//1.public表示方法是公开
//2.void:表示方法没有返回值
//3.speak():speak是方法名,()形参列表
//4.{}方法体,可以写我们要执行的代码
//5.System.out.println("我是一个好人"),表示我们的方法就是输出一句话
public void speak()
{
System.out.println("我是一个好人");
}
//1.(int n)形参列表,表示当前有一个形参,可以接收用户的输入
public void cal01(int n)
{
int res = 0;
for(int i = 1; i <= n; i++) {
res += i;
}
System.out.println("cal01 方法计算结果=" + res);
}
//1.public表示方法是公开的
//2.int:表示方法执行后,返回一个int值
//3.getSum:方法名
//4.(int num1,int num2)形参列表,2个形参,可以接收用户传入的两个数
//5.return res:表示把res的值返回
public int getSum(int num1,int num2)
{
int res = num1 + num2;
return res;
}
}
成员方法的好处:
- 提高代码的复用性
- 可以将实现的细节封装起来,然后供其他用户来调用即可
方法调用机制原理
成员方法的定义
访问修饰符 返回数据类型 方法名(形参列表...){
//方法体语句;
return 返回值;
}
1)形参列表:表示成员方法输入
2)返回数据类型:表示成员方法输出,void表示没有返回值
3)方法主体:表示为了实现某一功能代码块
4)return语句不是必须的
注意事项和细节:
1)访问修饰符
- 作用是控制方法使用的范围
- 如果不写默认访问(有四种:public,protected,默认,private)
2)返回数据类型
- 一个方法最多有一个返回值
- 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
- 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为return值;而且要求返回值类型必须和return的值类型一致或兼容
- 如果方法是void,则方法体中可以没有return 语句,或者只写return
public class MethodDetail {
public static void main(String[] args) {
AA a = new AA();
int[] res = a.getSumAndSub(1, 4);
System.out.println("和=" + res[0]);
System.out.println("差=" + res[1]);
//细节: 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
byte b1 = 1;
byte b2 = 2;
a.getSumAndSub(b1, b2);//byte -> int
//a.getSumAndSub(1.1, 1.8);//double ->int(×)
//细节: 实参和形参的类型要一致或兼容、个数、顺序必须一致
//a.getSumAndSub(100);//× 个数不一致
a.f3("tom", 10); //ok
//a.f3(100, "jack"); // 实际参数和形式参数顺序不对
}
}
class AA {
//细节: 方法不能嵌套定义
public void f4() {
//错误
// public void f5() {
// }
}
public void f3(String str, int n) {
}
//1. 一个方法最多有一个返回值[思考,如何返回多个结果返回数组]
public int[] getSumAndSub(int n1, int n2) {
int[] resArr = new int[2]; //
resArr[0] = n1 + n2;
resArr[1] = n1 - n2;
return resArr;
}
//2. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
// 具体看getSumAndSub
//
//3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为return 值;
// 而且要求返回值类型必须和return 的值类型一致或兼容
public double f1() {
double d1 = 1.1 * 3;
int n = 100;
return n; // int ->double
//return d1; //ok? double -> int
}
//如果方法是void,则方法体中可以没有return 语句,或者只写return ;
//老韩提示:在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义
//,最好是见名知意
public void f2() {
System.out.println("hello1");
System.out.println("hello1");
System.out.println("hello1");
int n = 10;
//return ;
}
}
3)形参列表
- 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开
- 参数类型可以为任意类型,包含基本类型或引用类型
- 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
- 方法定义时的参数称为形式参数,简称形参;方法调用时传入的参数为实际参数,简称为实参,实参和形参的类型要一致或兼容、个数、顺序必须一致
4)方法体
- 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义。
成员方法调用细节说明:
- 同一个类中的方法调用:直接调用即可
- 跨类中的方法A调用B类方法,需要通过对象名调用,比如:对象名.方法名(参数)
- 跨类的方法调用和方法的访问修饰符相关
public class MethodDetail02 {
//编写一个main 方法
public static void main(String[] args) {
A a = new A();
//a.sayOk();
a.m1();
}
}
class A {
//同一个类中的方法调用:直接调用即可
//
public void print(int n) {
System.out.println("print()方法被调用n=" + n);
}
public void sayOk() { //sayOk 调用print(直接调用即可)
print(10);
System.out.println("继续执行sayOK()~~~");
}
//跨类中的方法A 类调用B 类方法:需要通过对象名调用
public void m1() {
//创建B 对象, 然后在调用方法即可
System.out.println("m1() 方法被调用");
B b = new B();
b.hi();
System.out.println("m1() 继续执行:)");
}
}
class B {
public void hi() {
System.out.println("B 类中的hi()被执行");
}
}
成员方法的传参机制
基本数据类型的传参机制
基本数据类型,传递的是值(值拷贝),形参的改变不影响实参
public class MethodParameter01 {
//编写一个main 方法
public static void main(String[] args) {
int a = 10;
int b = 20;
//创建AA 对象名字obj
AA obj = new AA();
obj.swap(a, b); //调用swap
System.out.println("main 方法a=" + a + " b=" + b);//a=10 b=20
}
}
class AA {
public void swap(int a,int b){
System.out.println("\na 和b 交换前的值\na=" + a + "\tb=" + b);//a=10 b=20
//完成了a 和b 的交换
int tmp = a;
a = b;
b = tmp;
System.out.println("\na 和b 交换后的值\na=" + a + "\tb=" + b);//a=20 b=10
}
}
具体分析:
引用数据类型的传参机制
引用类型传递的是地址(传递的也是值,但是值是地址),可以通过形参影响实参
代码:
public class MethodParameter02 {
//编写一个main 方法
public static void main(String[] args) {
//测试
B b = new B();
// int[] arr = {1, 2, 3};
// b.test100(arr);//调用方法
// System.out.println(" main 的arr 数组");
// //遍历数组
// for(int i = 0; i < arr.length; i++) {
// System.out.print(arr[i] + "\t");
// }
// System.out.println();
//测试
Person p = new Person();
p.name = "jack";
p.age = 10;
b.test200(p);
//测试题, 如果test200 执行的是p = null ,下面的结果是10
//测试题, 如果test200 执行的是p = new Person();..., 下面输出的是10
System.out.println("main 的p.age=" + p.age);//10000
}
}
class Person {
String name;
int age;
}
class B {
public void test200(Person p) {
//p.age = 10000; //修改对象属性
//思考
p = new Person();
p.name = "tom";
p.age = 99;
//思考
//p = null;
}
//B 类中编写一个方法test100,
//可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化
public void test100(int[] arr) {
arr[0] = 200;//修改元素
//遍历数组
System.out.println(" test100 的arr 数组");
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
System.out.println();
}
}
具体分析:
p = null 和 p = new Person()对应示意图:
练习:编写一个方法copyPerson,可以复制一个Person 对象,返回复制的对象。克隆对象, 注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
//编写一个main 方法
public static void main(String[] args) {
Person p = new Person();
p.name = "milan";
p.age = 100;
//创建tools
MyTools tools = new MyTools();
Person p2 = tools.copyPerson(p);
//到此p 和p2 是Person 对象,但是是两个独立的对象,属性相同
System.out.println("p 的属性age=" + p.age + " 名字=" + p.name);
System.out.println("p2 的属性age=" + p2.age + " 名字=" + p2.name);
//这里老师提示: 可以同对象比较看看是否为同一个对象
System.out.println(p == p2);//false
}
}
class Person {
String name;
int age;
}
class MyTools {
//编写一个方法copyPerson,可以复制一个Person 对象,返回复制的对象。克隆对象,
//注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
//
//编写方法的思路
//1. 方法的返回类型Person
//2. 方法的名字copyPerson
//3. 方法的形参(Person p)
//4. 方法体, 创建一个新对象,并复制属性,返回即可
public Person copyPerson(Person p) {
//创建一个新的对象
Person p2 = new Person();
p2.name = p.name; //把原来对象的名字赋给p2.name
p2.age = p.age; //把原来对象的年龄赋给p2.age
return p2;
}
}
方法递归调用
基本介绍:
简单的说:递归就是方法自己调用自己,每次调用时传入不同的变量,递归有利于编程者解决复杂问题,使代码变得简洁
举例:
1)打印问题
2)递归问题
代码:
public class Recursion01 {
//编写一个main 方法
public static void main(String[] args) {
T t1 = new T();
t1.test(4);//输出什么? n=2 n=3 n=4
int res = t1.factorial(5);
System.out.println("5 的阶乘res =" + res);
}
}
class T {
//分析
public void test(int n) {
if (n > 2) {
test(n - 1);
}
System.out.println("n=" + n);
}
//factorial 阶乘
public int factorial(int n) {
if (n == 1) {
return 1;
} else {
return factorial(n - 1) * n;
}
}
}
具体分析:
递归的重要规则:
1)执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2)方法的局部变量是独立的,不会相互影响,比如n变量
3)如果方法中使用的是引用变量类型(比如数组),就会共享该引用类型的数据
4)递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,栈溢出
5)当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
方法重载
基本介绍:
Java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
比如:System.out.println(); out 是PrintStream 类型
重载的好处:
- 减轻了起名的麻烦
- 减轻了记名的麻烦
案例:
class MyCalculator {
//下面的四个calculate 方法构成了重载
//两个整数的和
public int calculate(int n1, int n2) {
System.out.println("calculate(int n1, int n2) 被调用");
return n1 + n2;
}
//没有构成方法重载, 仍然是错误的,因为是方法的重复定义
// public void calculate(int n1, int n2) {
// System.out.println("calculate(int n1, int n2) 被调用");
// int res = n1 + n2;
// }
//看看下面是否构成重载, 没有构成,而是方法的重复定义,就错了
// public int calculate(int a1, int a2) {
// System.out.println("calculate(int n1, int n2) 被调用");
// return a1 + a2;
// }
//一个整数,一个double 的和
public double calculate(int n1, double n2) {
return n1 + n2;
}
//一个double ,一个Int 和
public double calculate(double n1, int n2) {
System.out.println("calculate(double n1, int n2) 被调用..");
return n1 + n2;
}
//三个int 的和
public int calculate(int n1, int n2,int n3) {
return n1 + n2 + n2;
}
}
注意细节:
1)方法名:必须相同
2)参数列表:必须不同(参数类型或个数或顺序,至少有一样不同,参数名无要求)
3)返回类型:无要求
练习:
/*
编写程序,类Methods 中定义三个重载方法并调用。方法名为m。
三个方法分别接收一个int 参数、两个int 参数、一个字符串参数。分别执行平方运算并输出结果,
相乘并输出结果,输出字符串信息。在主类的main ()方法中分别用参数区别调用三个方法
定义三个重载方法max(),第一个方法,返回两个int 值中的最大值,
第二个方法,返回两个double 值中的最大值,第三个方法,
返回三个double 值中的最大值,并分别调用三个方法
*/
class Methods {
//分析
//1 方法名max
//2 形参(int,int)
//3.int
public int max(int n1, int n2) {
return n1 > n2 ? n1 : n2;
}
//分析
//1 方法名max
//2 形参(double,double)
//3.double
public double max(double n1, double n2) {
return n1 > n2 ? n1 : n2;
}
//分析
//1 方法名max
//2 形参(double,double,double)
//3.double
public double max(double n1, double n2, double n3) {
System.out.println("max(double n1, double n2, double n3)");
//求出n1 和n2 的最大值
double max1 = n1 > n2 ? n1 : n2;
return max1 > n3 ? max1 : n3;
}
public double max(double n1, double n2, int n3) {
System.out.println("max(double n1, double n2, int n3)");
//求出n1 和n2 的最大值
double max1 = n1 > n2 ? n1 : n2;
return max1 > n3 ? max1 : n3;
}
//分析
//1 方法名m
//2 形参(int)
//3.void
public void m(int n) {
System.out.println("平方=" + (n * n));
}
//1 方法名m
//2 形参(int, int)
//3.void
public void m(int n1, int n2) {
System.out.println("相乘=" + (n1 * n2));
}
//1 方法名m
//2 形参(String)
//3.void
public void m(String str) {
System.out.println("传入的str=" + str);
}
}
可变参数
基本介绍:
Java中允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法
基本语法:
访问修饰符 返回类型 方法名(数据类型...形参名){
}
案例:
public class VarParameter01 {
//编写一个main 方法
public static void main(String[] args) {
HspMethod m = new HspMethod();
System.out.println(m.sum(1, 5, 100)); //106
System.out.println(m.sum(1,19)); //20
}
}
class HspMethod {
//可以计算2 个数的和,3 个数的和, 4. 5, 。。
//可以使用方法重载
// public int sum(int n1, int n2) {//2 个数的和
// return n1 + n2;
// }
// public int sum(int n1, int n2, int n3) {//3 个数的和
// return n1 + n2 + n3;
// }
// public int sum(int n1, int n2, int n3, int n4) {//4 个数的和
// return n1 + n2 + n3 + n4;
// }
//.....
//上面的三个方法名称相同,功能相同, 参数个数不同-> 使用可变参数优化
//1. int... 表示接受的是可变参数,类型是int ,即可以接收多个int(0-多)
//2. 使用可变参数时,可以当做数组来使用即nums 可以当做数组
//3. 遍历nums 求和即可
public int sum(int... nums) {
//System.out.println("接收的参数个数=" + nums.length);
int res = 0;
for(int i = 0; i < nums.length; i++) {
res += nums[i];
}
return res;
}
}
注意事项:
1)可变参数的实参可以是0个或者任意多个
2)可变参数的实参可以是数组
3)可变参数的本质是数组
4)可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
5)一个形参列表中只可能出现一个可变参数
作用域
基本介绍:
面向对象中,变量作用域是非常重要的知识点
-
在Java编程中,主要的变量就是属性(成员变量)和局部变量
-
我们说的局部变量一般是指在成员方法中定义的变量
-
Java中作用域的分类
- 全局变量:也就是属性,作用域为整个类体
- 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
-
全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
代码:
public class Demo03 {
public static void main(String[] args) {
}
}
class Dog {
//全局变量:也就是属性,作用域为整个类体
//属性在定义时,可以直接赋值
int age = 10;
//全局变量(属性)可以不赋值,直接使用,因为有默认值
double weight; //默认值为0.0
//局部变量必须赋值后才能使用,因为没有默认值
public void h1(){
int num = 1;
System.out.println("num="+num);
}
public void cry(){
//1.局部变量一般是指在成员方法中定义的变量
//2.n和name就是局部变量
//3.n和name的作用域在cry方法中
int n = 10;
String name = "jack";
System.out.println("在cry方法中使用属性age="+age);
}
public void eat(){
System.out.println("在eat方法中使用属性age="+age);
}
}
注意事项:
1)属性和局部变量可以重名,访问时遵循就近原则
2)在同一个作用域下,比如在同一个成员方法中,两个局部变量,不能重名
3)属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡,局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡,即在一个方法调用过程中
4)作用域范围不同
- 全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
- 局部变量:只能在本类中对应的方法中使用
5)修饰符不同
- 全局变量/属性可以加修饰符
- 局部变量不可以加修饰符
public class VarScopeDetail {
//编写一个main 方法
public static void main(String[] args) {
Person p1 = new Person();
/*
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
局部变量,生命周期较短,伴随着它的代码块的执行而创建,
伴随着代码块的结束而销毁。即在一次方法调用过程中
*/
//p1.say();//当执行say 方法时,say 方法的局部变量比如name,会创建,当say 执行完毕后
//name 局部变量就销毁,但是属性(全局变量)仍然可以使用
//
T t1 = new T();
t1.test(); //第1 种跨类访问对象属性的方式
t1.test2(p1);//第2 种跨类访问对象属性的方式
}
}
class T {
//全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
public void test() {
Person p1 = new Person();
System.out.println(p1.name);//jack
}
public void test2(Person p) {
System.out.println(p.name);//jack
}
}
class Person {
//细节: 属性可以加修饰符(public protected private..)
// 局部变量不能加修饰符
public int age = 20;
String name = "jack";
public void say() {
//细节属性和局部变量可以重名,访问时遵循就近原则
String name = "king";
System.out.println("say() name=" + name);
}
public void hi() {
String address = "北京";
//String address = "上海";//错误,重复定义变量
String name = "hsp";//可以
}
}
构造器
一个需求:前面我们在创建人类的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值,如果现在要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,这时候就可以使用构造器
基本语法:
[修饰符] 方法名(形参列表){
方法体;
}
说明:
- 构造器的修饰符可以默认,也可以是public,protected, private
- 构造器没有返回值
- 方法名和类名名字必须一样
- 参数列表和成员方法一样的规则
- 构造器的调用由系统完成
基本介绍:
构造方法又叫构造器,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化
具体特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化
public class Constructor01 {
//编写一个main 方法
public static void main(String[] args) {
//当我们new 一个对象时,直接通过构造器指定名字和年龄
Person p1 = new Person("smith", 80);
System.out.println("p1 的信息如下");
System.out.println("p1 对象name=" + p1.name);//smith
System.out.println("p1 对象age=" + p1.age);//80
}
}
//在创建人类的对象时,就直接指定这个对象的年龄和姓名
//
class Person {
String name;
int age;
//构造器
//老韩解读
//1. 构造器没有返回值, 也不能写void
//2. 构造器的名称和类Person 一样
//3. (String pName, int pAge) 是构造器形参列表,规则和成员方法一样
public Person(String pName, int pAge) {
System.out.println("构造器被调用~~ 完成对象的属性初始化");
name = pName;
age = pAge;
}
}
注意事项:
1)一个类可以定义多个不同的构造器,即构造器重载
2)构造器名和类名要相同
3)构造器没有返回值
4)构造器是完成对象的初始化,并不是创建对象
5)在创建对象时,系统自动的调用该类的构造方法
6)如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)
7)一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下
class Dog {
//如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)
//使用javap 指令反编译看看
/*
默认构造器
Dog() {
}
*/
//一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,
//除非显式的定义一下,即: Dog(){} 写(这点很重要)
//
public Dog(String dName) {
//...
}
Dog() { //显式的定义一下无参构造器
}
}
对象创建流程
一个案例:
class Person{
int age = 90;
String name;
Person(String n,int a){ //构造器
name = n; //给属性赋值
age = a;
}
}
具体分析:
1)加载Person类信息(Person.class),只会加载一次
2)在堆中分配空间(地址)
3)完成对象初始化
- 默认初始化 age = 0,name = null
- 显示初始化 age=90 name = null
- 构造器的初始化:age = 20,name =“小倩”
4)在对象在堆中的地址,返回给p(p是对象名,也可以理解为对象的引用)
this关键字
问题引入:
class Dog1{
String name;
int age;
// public Dog1(String dName,int dage){ //构造器
// name = dName;
// age = dage;
// }
//如果我们出现构造器的形参,能够直接写成属性名,就更好了
//但是出现了一个问题,根据变量的作用域原则
//构造器的name是局部变量,而不是属性
//构造器的age 是局部变量,而不是属性
//==>引出this关键字来解决
public Dog1(String name,int age)
{
name = name;
age = age;
}
基本介绍:
Java虚拟机会给每个对象分配this,代表当前对象
简单的说:哪个对象调用,this就代表哪个对象
理解:
class Dog{
public String name;
public int age;
public Dog(String name,int in_age){
this.name = name;
this.age = in_age;
}
public void info(){
System.out.println(this.name+"\t"+this.age+"\t"+"当前对象的hashcode是:"+this.hashCode());
}
}
注意事项:
1)this关键字可以用来访问本类的属性、方法、构造器
2)this用于区分当前类的属性和局部变量
3)访问成员方法的语法:this.方法名(参数列表)
4)访问构造器语法:this(参数列表):注意只能在构造器中使用,即只能在构造器中访问另外一个构造器,必须放在第一条语句
5)this不能在类定义的外部使用,只能在类定义的方法中使用
public class Demo05 {
public static void main(String[] args) {
// T t1 = new T();
// t1.f2();
T t2 = new T();
}
}
class T{
String name = "jack";
int num = 100;
/*
细节:访问构造器语法:this(参数列表);
注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)
注意:如果有this(参数列表),必须放在第一条语句
*/
public T(){
this("jack",100);
//这里去访问T(String name,int age)
System.out.println("T()构造器");
}
public T(String name,int age){
System.out.println("T(String name,int age)构造器");
}
//this关键字可以用来访问本类的属性
public void f3(){
String name = "smith";
//传统方式
System.out.println("name="+name+"num="+num);//根据就近原则,输出smith,100
//也可以使用this访问属性
System.out.println("name="+this.name+"num="+this.num);//输出jack,100
}
//访问成员方法的语法:this.方法名(参数列表)
public void f1(){
System.out.println("f1()方法...");
}
public void f2(){
System.out.println("f2()方法...");
//调用本类的f1
//第一种方式
f1();
//第二种方式
this.f1();
}
}