类和对象的本质
文章目录
类的本质
8种基本数据类型,当我们需要什么类型,即把它自定义出来
class 自定义数据类型 User
过程中结合属性和方法进行描述,类中不能调用操作
使用: int i = 10; 使用i就必须声明(包括数据类型)和初始化,进一步在程序中使用变量i参与运算
User user(类类型产生的变量,对象) = new User();
User是类型,user是一个对象,经过赋值,user对象就进行了实例化,所以,对象user是User的一个实例 ,
赋值过程: new User();
如果没有经过new赋值, 那么对象会存在一个默认值: null
类类型(class声明的)也叫做对象类型
对象类型有三种:
- class声明的自定义的类
- String类型(本质上还是class类型)
- 数组 int\[\] arr = new int\[length\]; 因为对象类型可以定义属性和方法(为了得到length)
对象的本质
对象的本质,是类的实例(instance/ initialize 实例化 )
实例化的过程: 通过 new className() 分配内存, 再将内存首地址传递给对象 ,所以,对象是引用了一段内存的首地址
所以, 对象类型也可以叫做引用(reference)类型
Reference: 引用, 相当于电视的遥控器
如果对象没有初始化(未引用地址,此时对象保存的是默认值null), 而null是不能去执行操作的,如果执行则抛出异常: NullPointerException
为什么不能用 new User() 直接作为操作的对象?
new User() 虽然不能做二次操作, 但如果只执行一次? 可以的
类和对象的关系
类是公共的模板,对象是具体的个体
类是公共的类型,对象是实例
类负责描述,对象负责执行
类和对象在内存中的图例
堆内存和栈内存: 堆的速度慢不适合反复操作 , 栈适合快速读写和适合反复操作
通过对象直接给对象赋值以及对象为null的情况
public static void main(String[] args) {
// 用User生成对象
User user1 = new User();
user1.name = "任心悦";
User user2 = user1;
user2.name = "姜志伟";
System.out.println("user1:"+user1);
System.out.println("user2:"+user2);
System.out.println("user2.name:"+user2.name); //姜志伟
System.out.println("user1.name:"+user1.name); //姜志伟
// new User() 也是一个垃圾内存
// 应用场景: 只需要调用一次操作时适合使用(窗体swing)
System.out.println(new User());
new User().name = "张学友";
System.out.println(new User().name); //null 字符串的默认值
// null是所有对象类型的默认值
User user3 = null ;
System.out.println("user3:"+user3);
// java规定null对象不能执行调用操作,否则抛出NullPointerException
user3.name = "张曼玉";
}
new 关键字
-
调用构造方法
-
开辟内存
-
传递引用: 传递引用给对象
构造方法
User()
概念
方法名和类名完全一致(包括大小写),没有返回值的一个方法,定义类当中
使用
只能通过new 来调用, 在对象被实例化的时候
特点:
- 每一个类都有一个默认的构造方法,形式:
ClassName(){}
-
默认构造方法(隐式构造方法)可以被覆盖, 覆盖它的构造方法称为显式构造方法
public class User { // 用户编号 String id ; // 用户姓名 String name; // 用户手机 String phone; // 用户密码 String pwd; //用户年龄 int age; //int i ; // 主动创建一个构造方法: /* * User(){ System.out.println("这是一个User的构造方法"); } */ // 通过构造方法的参数给类中的成员字段赋初值 User(String name,String phone,int age){ // 在构造方法中给i赋值 //i = 10; this.name = name; this.age = age; this.phone = phone; } // 如果要使用空参的构造方法, 利用重载 // 重载: 类中存在多个方法,它们的方法名相同,参数不同, //它们之间就形成了重载,用overload表示 User(){ // 构造方法之间可以互相调用: this() // 调用带phone参数的构造方法: this("13312345678"); } User(String phone){ this.phone = phone; } }
构造方法的重载
重载的概念
类中存在多个方法,它们的方法名相同,参数不同,它们之间就形成了重载,用overload表示
下面, 我们利用重载的概念对构造方法进行加工:
// 通过构造方法的参数给类中的成员字段赋初值
User(String name,String phone,int age){
// 在构造方法中给i赋值
//i = 10;
this.name = name;
this.age = age;
this.phone = phone;
}
// 如果要使用空参的构造方法, 利用重载
// 重载: 类中存在多个方法,它们的方法名相同,参数不同,
//它们之间就形成了重载,用overload表示
User(){
// 构造方法之间可以互相调用: this()
// 调用带phone参数的构造方法:
this("13312345678");
}
User(String phone){
this.phone = phone;
}
作用:
-
让对象能够被实例化(实操中主要的职能)
-
将对象一开始就需要执行的操作定义在构造方法中
-
通过参数给属性赋初值(非必要)
类中的全局和局部作用域:
定义在方法的参数/内部的变量name是局部的作用域(只能在方法内访问)
如果全局和局部具有相同的名称的变量, 局部变量的优先级更高(就近原则)
java中使用this 区分属性和方法中的同名变量
// 用户姓名
String name;
// 用户手机
String phone;
//用户年龄
int age;
// 通过构造方法的参数给类中的成员字段赋初值
User(String name,String phone,int age){
this.name = name;
this.age = age;
this.phone = phone;
}
this关键字
关键字, 作用相当于对象,只能在类的内部的方法中使用, 可以表示对象
作用
用于区分全局和局部同名的内容,当通过this调用的内容都是类中定义的
// 通过构造方法的参数给类中的成员字段赋初值
User(String name,String phone,int age){
// 在构造方法中给i赋值
//i = 10;
this.name = name;
this.age = age;
this.phone = phone;
}
植物大战僵尸的设计
public class Zombie {
// 字段(属性)
// 攻击力
int attack;
// 防御力
int defense;
// 生命值
int hp;
// 名字
String name;
// 方法
// 构造方法
Zombie(String name, int attack, int defence, int hp) {
this.name = name;
this.attack = attack;
this.defense = defence;
this.hp = hp;
}
/**
*再增加一个无参的构造方法
*让对象在创建的时候更加灵活
*/
Zombie() {
}
/**
* 当僵尸遭遇植物,近战方法
* 业务: 僵尸攻击,植物防御,
* 攻击次数持续,直到植物的hp<=0,战斗结束
*/
void fight(Plant plant) {
// 在当前类中,可以用this表示自己的对象
System.out.println(this.name+"开始攻击"+plant.name);
while (plant.hp>0) {
plant.hp -= this.attack-plant.defense;
System.out.println(plant.name+"还剩"+plant.hp+"血");
// 通过Thread.sleep方法让程序停顿
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("战斗结束,"+plant.name+"被"+this.name+"干掉了");
}
}
java中的三种引用类型
8种基本数据类型是普通数据类型
自定义的类和jdk中已经存在的类型都可以成为对象类型,作用是为了创建对象
对象本质是对内存首地址的引用,对象类型也才被称为引用类型(refence)
-
类(首字母大写)
-
数组
-
String
public static void main(String[] args) {
// 声明一个int的数组
int[] arr = new int[10];
System.out.println(arr[0]);
System.out.println(Arrays.toString(arr));
//[I@2401f4c3
System.out.println(arr);
}
对象类型赋值的特点,对象类型传递的是内存地址的引用,所以当对象类型之间赋值的时候,这种引用关系会继续传递,本质上是同一个对象
public static void main(String[] args) {
//先判断栈内存中是否有1的存在,如果有则将该内存指向给a
//如果没有,就开辟
//b赋值的时候,判断
int a = 1; //字面值
int b = a; //
System.out.println("b:"+b);
b=2;
System.out.println("b:"+b);
System.out.println("a:"+a);
//换成类型:Plant
Plant p1 = new Plant();
p1.name="高坚果";
Plant p2 = p1;
System.out.println(p2.name);
p2.name = "窝瓜";
System.out.println(p2.name);
System.out.println(p1.name);
}
String字符串类型虽然也是对象类型, 但java对它做了优化
具体对比情况参考如下代码:
//String传递引用
/*
* String str1 = "abc"; String str2 = str1; str2 = "abcd";
* System.out.println("str1:"+str1);
*/
/*
* String str1 = "abc"; String str2 = "abc";
* System.out.println(str1.equals(str2)); //true
*/
/*
* String str1 = "abc"; String str2 = "abcd";
* System.out.println(str1.equals(str2)); //false
*/
// 当字符串使用new String()的时候, 就不再是在常量池中引用,而是在堆中
String str1 = new String("abc");
String str2 = new String("abc");
// == 对象类型使用连等, 比的是内存首地址是否相同
System.out.println(str1 == str2); //false ,
//equals 才是比较内容的方法
System.out.println(str1.equals(str2)); //true
String str11 = "abc";
String str22 = "abc";
System.out.println(str11 == str22); // true
System.out.println(str11.equals(str22)); // true
String str111 = "abc";
String str222 = new String("abc");
System.out.println(str111 == str222); // false
System.out.println(str111.equals(str222)); // true
// java在String常量中的优化
String str1111 = "abc";
String str2222 = "ab"+"c";
System.out.println(str1111 == str2222); // true
System.out.println(str1111.equals(str2222)); // true
String在内存中的表现情况
java把字符串的值存放在常量池中, 目的是, 加快字符串的访问速度
下面代码中会生成几个String对象?
//创建了几个字符串对象?
// 答案为2 : "abc"在常量池生成1个, string也是1个
String string = new String("abc");
// 答案为3 : "abc"在常量池生成1个, str1和str2各一个, 共3个
String str1 =new String ("abc");
String str2 =new String ("abc");
final的作用
-
声明变量时,表示常量
-
声明类, 不能被继承(在继承的章节来学习)
-
声明方法, 不能被重写(在多态的章节来学习)
public class Demo3 {
// final修饰变量, 变为常量, 常量必须赋初值, 之后不可改变
// 常量一般都建议用全大写命名
//final int i = 1;
//final int I = 1;
final double PI = 3.14;
public static void main(String[] args) {
Demo3 demo3 = new Demo3();
// demo3.i = 2;
}
}