类 和 对象
文章目录
每博一文案
让你受伤的往往不是别人,而是你自己的心。
别人言语讽刺你,以便自己生闷气,别人出口污蔑你,你就感到非常委屈。
出门不小心被石头绊了一跤,或者错过了公交,导致迟到都会让你自愿质疑,不停抱怨,满身的缠绕着赋能量。
但是你有没有想过,你之所以身体受伤,是因为你把别人污蔑你的话听进了你的心里,明知道这些话不是真的,你为什么还把他放在心里,不小心被石头绊倒或者迟到,
也只是短时间的惊慌不快,何必要将这种薄块继续发酵,导致接下来的一天都很难受呢。
—————— 一禅心灵庙语
类 和 对象
- 类就相当是一个模板,与C语言中的结构体类似,不过 Java 中的类更加的多样,更加的复杂,存在着更多的属性,而对象就是通过这个类的模板产生出来的样本,所以一个类可以产生多个对象,但是每个对象,都是相互独立的,互不干扰的,关于这一点后面会详细介绍的,
- 声明一个类就是创建一个新的数据类型,是和C语言中的结构体设定差不多的,而类在Java当中是属于引用类型的
- 注意一个类就会生成一个类文件(class),所以一般都是一个文件中一个类的
- 不可以把一个同名的类,放在同一个文件夹中(或者同一个目录中区去),原因很简单:因为会生成类文件的,而我们的 windows 的在同一个文件夹中不可存放两个同名的文件的
类的编写
先编写一个结构简单的类看看,步骤如下:
- 在Java中我们使用 关键字 class 声明一个类,
- 给类起一个见名知意的名字,首字母大写,遵循大驼峰的命名原则
- 编写类的特征,特征即类的属性部分又称为(字段)
- 编写类的函数(方法),即类的成员方法
public class Person {
// 类的属性,字段
int age; // 年龄
String name; // 姓名
// 方法(行为),成员方法
public void study() {
System.out.println("青,取之于蓝,而胜于蓝,冰,水为之,而寒于水。");
}
}
类的实例化 (对象的创建和使用)
- 到这里,问你一个问题,你有对象吗?呵呵
- 如果没有,没关系,我们来 new 出一个对象
- 使用 new 运算符创建的类类型的“主体”,称为实例 ,创建实例的操作称为实例化
- 类的实例,数组的主体统称为 对象 ,类的实例和数组的主体都是使用 new 动态创建的,所谓的对象,就是对程序运行时动态创建的主体的总称。
- 在编写好以后,我们就可以创建对应类的对象了,使用 new 运算符,然我们来,new 一个对象吧,格式如下:
类名 对象名 = new 类名( );
public class Person {
// 类的属性,字段
int age; // 年龄
String name; // 姓名
// 方法(行为),成员方法
public void study() {
System.out.println("青,取之于蓝,而胜于蓝,冰,水为之,而寒于水。");
}
public static void main(String[] args) {
Person person = new Person(); // 创建对象,实例化,new 一个对象
}
}
字段,方法 的访问
- 给对象的属性赋值:
对象名.属性名 = 值;
- 调用对象中的方法:
对象名.方法名( );
-
使用 . (逗号) 运算符;访问对象中的字段,以及方法
-
这里的**“访问”** 既包含读,也包含了写
-
对于没有赋予初值的字段,有默认初始值
-
默认值规则:
- 对于各种数值类型,默认值为 0,和 0,0
- 对于 boolean 类型,默认值为 false
- 对于引用类型 ( String , Arrays ,自定制类),默认值为 null
-
-
null 在 Java 中表示 “空引用” ,表示不引用任何对象,类似于C语言中的空指针,如果对 null 进行,. 的操作会引起异常
class Person {
// 类的属性,字段
int age; // 年龄
String name; // 姓名
// 方法(行为),成员方法
public void study() {
System.out.println("青,取之于蓝,而胜于蓝,冰,水为之,而寒于水。");
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person(); // 创建对象,实例化,new 一个对象
person.name = "你好世界";
person.age = 18;
System.out.println("name:"+person.name);
System.out.println("age:"+person.age);
person.study();
}
}
- 结果:
未初始化的字段(成员变量)
- 默认值规则:
- 对于各种数值类型,默认值为 0,和 0,0
- 对于 boolean 类型,默认值为 false
- 对于 char 类型,默认值为 ;\u0000
- 对于引用类型 ( String , Arrays ,自定制类),默认值为 null
class BeginNum {
char cNum;
int iNum;
float fNum;
boolean bNum;
String str;
}
public class Main {
public static void main(String[] args) {
BeginNum beginNum = new BeginNum(); // 实例化,new创建一个对象
System.out.println("cNum:"+beginNum.cNum);
System.out.println("iNum:"+beginNum.iNum); // 0
System.out.println("fNum:"+beginNum.fNum); // 0.0
System.out.println("bNum:"+beginNum.bNum); // false
System.out.println("sNum:"+beginNum.str); // null
}
}
- 结果:
类中的内存视图:
class Person1 {
String name;
int age;
public void show() {
System.out.println("我叫"+name+age+"岁");
}
}
public class Main {
public static void main(String[] args) {
Person1 person1 = new Person1();
person1.age = 18;
person1.name = "小华";
person1.show();
}
}
对应的内存分析图:
static 关键字
- 这里简单的使用
- 修饰属性
- 修饰方法
- 修饰代码块
- 修饰类
- 这里我们先学习修饰属性,和方法
类变量
- 类变量 和 类类型变量 是不一样的,请注意它们的含义,不要混淆了。
- 类变量: 的另一个名字就是静态字段
- 类类型变量:我们自己对于描述复杂对象,创建的类型
静态字段 和 非静态字段
静态字段
- 被static修饰的的字段,是类变量,也叫静态字段
- 同一个类的实例之间共享的数据的静态字段,即类变量
- 只要在字段声明时加上,static 关键字,就可以实现,共享,由于类中共享的变量,因此被称为类变量。另外,由于声明时加上了static ,因此也被称为静态字段。
- 注意:Java静态属性和类相关,和具体的实例无关,换句话说。同一个类的不同实例共用同一个静态属性,再换句话说:就是被 static 关键字修饰的字段,不是属于各个实例的数据,而是属于类的所有实例共享的数据
- 被 static 修饰的字段——静态字段,是和类一起被加载到内存的,只会加载一次到内存中,所有实例化(对象)一起共用它这一个
- 静态字段的访问格式:
类名.静态字段名 = 值;
非静态字段
- 这个简单,不被 static 关键字修饰的字段,就被称为非静态关键字
- 非静态关键字是和对象相关的,所以每个实例化对象中的非静态字段是私有的,不一样的,不同的对象对应各自的非静态字段,
- 是非静态字段是在实例化(对象)后,才会加载到内存中的,比静态的慢
class Test {
int num; // 非静态字段
static int count; // 静态的字段
}
public class Main {
public static void main(String[] args) {
Test test1 = new Test();
test1.num++;
Test.count++;
System.out.println("test1:"+test1.num); // 1
System.out.println("count:"+Test.count); // 1
/* 静态的字段的访问方式: 类名.静态字段*/
Test test2 = new Test(); // 实例化两个对象
test2.num++;
Test.count++;
System.out.println("test2:"+test2.num); // 1
System.out.println("count"+Test.count); // 2
}
}
- 结果:
- 我们可以通过结果可以看到 非静态的字段,在不同的对象中各自相互独立的,互补干扰
- 而静态的则是所有实例化对象共有的
类方法
- 同样的被 static 修饰的方法,静态方法又称为:类方法
非静态方法 和 静态方法
- 同静态字段一样的,所以这里就详细介绍了
静态方法
- 被 static 关键字修饰的方法,叫做类方法,又称 静态方法
- 同样静态方法,和类有关,是和类一起加载到内存的,只会加载一次,与所有实例共用该方法
- 可以直接调用方法,而无需创建类的实例
- 不过这里好像并不常用的
- 调用静态方法的格式:
类名.静态方法名( );
非静态方法
-
同理,不被 static 关键字修饰的方法,可以称为非静态方法
-
同样非静态方法,和实例化对象有关,在实例化后,非静态方法,才被加载到内存当中去的
class Demo {
public static void test() {
System.out.println("静态方法");
}
}
public class Main {
public static void main(String[] args) {
// 调用静态方法 类名+静态方法名
Demo.test();
}
}
- 结果:
静态 与 非静态 有趣关联
-
被关键字 static 修饰的方法,字段,称为 静态方法
-
没有被关键字 static 修饰的方法,字段称为:非静态方法
-
静态 与 非静态 有趣关联
- 静态之间可以相互访问(类名+静态方法,类名+静态属性),但是静态的不可以直接访问非静态的,如果非要访问的话,要实例化,再通过对象引用才可以访问非静态的,不然会报错的。
- 非静态之间可以相互访问,以及还可以直接访问静态的
- 简单的说明一下原因:
- 前面我说过:静态的是和类一起加载到内存的,非静态的是实例化后,才被加载到内存当中的,这里可以看出静态的是比非静态的先加载到内存当中的,而我们静态的访问非静态的时候,非静态的还没有加载到内存中(也就是还没有创建起来) ,我们就是在访问一个还没有加载到内存中的数据(未创建的数据),而我们的程序都要加载到内存中才能运行的,我们 Java中的 JVM 虚拟机是不会允许这样的事情发生的 ,直接暴力点,给你来个报错.
- 想要详细了解的,请移步到这里:Java 中为什么静态的无法访问非静态的
- 非静态的访问,静态以及非静态的方法,属性的实例
class Demo {
int num1 = 10;
static int num2 = 100;
public void test1() {
System.out.println("num1:"+num1); // 非静态之间可以互相访问
System.out.println("num2:"+num2); // 非静态的可以访问静态的属性
System.out.println("非静态方法");
test2(); // 非静态可以直接访问静态的方法
}
public static void test2() {
System.out.println("静态方法");
}
}
public class Main {
public static void main(String[] args) {
Demo demo = new Demo(); // new 实例化对象
demo.test1();
}
}
- 结果:
- 静态的访问 静态,以及非静态的
小结:
class Person {
// 类的属性,字段
int age; // 年龄
String name; // 姓名
String sex;
public static int count; // 类变量也叫静态变量,编译时已经产生了,属于类本身,且只有一份,存放在方法区中
public final int SIZE = 10; // 被final 修饰的叫常变量,不是静态的,也属于对象,后续不可以更改的
public static final int COUNT = 99; // 静态常量,属于类本身,只有一份,被final 修饰,后续不可更改
// 实例成员方法
public void eat() {
int a = 10; // 局部变量
System.out.println("eat()!");
}
// 实例成员方法
public void sleep() {
System.out.println("sleep()!");
}
// 静态成员方法
public static void staticTest() {
/*静态成员不能访问非静态成员
sex = "mua"; error
*/
System.out.println("staticTest()!");
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person(); // 产生对象,实例化对象
System.out.println(person.age);
System.out.println(person.name);
System.out.println(Person.COUNT); // 类变量的访问:类名+属性名(方法名)
System.out.println(Person.count);
person.eat();
person.sleep();
}
}
- 结果:
- 内存图示:
private 实现封装
- 类的使用者 和 类的调用者
- private / public 这两个关键字表示**”访问权限的控制“**
- 被 public 修饰的成员变量或者成员方法,公开的可以直接被类的调用者使用
- 被 private 修饰的成员变量或者成员方法,不能被类的调用者使用,只能在本类中调用使用
直接使用public
class Person3 {
public String name = "小华";
public int age = 18;
}
public class Main {
public static void main(String[] args) {
Person3 person3 = new Person3(); // 产生对象,实例化对象
System.out.println("我叫"+person3.name+"今年"+person3.age+"岁");
}
}
- 结果:
private 封装
- 通过 pricvate 封装将属性进行私有化,
- 再编写公有方法,利用 public 进行修饰,提供属性对应的设置和获取方法
- 再赋值方法中根据实际需求进行合理的限制,提高代码的安全性
class Person2 {
private String name = "小华"; // private 只能再本类中可以被访问到
private int age = 18; // private 只能再本类中可以被访问
public void show() { // public 公有的一个接口,通过该接口,访问被 private私有化的属性
System.out.println("name:"+name);
System.out.println("age:"+age);
}
}
public class Main {
public static void main(String[] args) {
Person2 person2 = new Person2();
/* System.out.println(person2.name); // 报错无法访问,private的属性
System.out.println(person2.age); // private 修饰的属性,只能在对应的类中访问*/
person2.show();
}
}
- 结果:
getter(获得) / setter(设置) 方法
- private 不光能修饰字段,也能修饰方法
- 通常情况下,我们会把字段设为private 属性,但是方法是否需要设为 public ,就需要看具体情形而定,一般我们希望一个类只提供必要的public 方法,而不应该把所有的方法都无脑的设为 public
- 当我们使用private 来修饰字段的时候,就无法直接使用这个字段了
- 使用:
- getter 方法,表示获取这个成员的值:的一个统称
- setter 方法,表示设置这个成员的值:的一个统称
- 不是所有的字段都一定要提供 setter / getter 方法 ,而是根据实际情况决定提供那种方法,有的时候是 setter 和 getter 一起使用的
- 在 IDEA 中可以使用 Alt + Insert (或者 Alt _ F12 ) 快速生成 **setter / getter ** 方法,在VSCode 中可以使用鼠标右键 ——> 菜单——> 源代码操作 中自动生成 setter / getter 方法
-
操作:同时按 键盘中的 Alt+ Insert 键,弹出左边的窗口:
-
选择其中的 **Getter and Setter **, 或者你只需要其中的某一个,选中它,点击一下
-
还可以鼠标右键——> 弹出下面右边的窗口图 ——> 点击其中的 ——> Generate ——> 弹出和下面左边的图的窗口是一致的
- 最后点击 OK 就会自动帮你生成你需要的 setter / getter 方法
class Person6 {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String name;
private int age;
}
public class Main {
public static void main(String[] args) {
Person6 person6 = new Person6();
person6.setName("你好世界");
person6.setAge(18); // 设置
String name = person6.getName();
int age = person6.getAge(); // 获取
System.out.println(name);
System.out.println(age);
}
}
- 结果:
- 注意事项:
- 上述代码中的 getName 和 getAge 方法就是 getter 方法,表示获取这个成员的值
- 上述代码中的 setName 和 setAge 方法就是 setter 方法,表示设置这个成员的值
this. 关键字
- this. 关键字指代对象自身,通过this. 关键字可以指向自身地址空间,简要内存分析如下图:
- 这里我们现在只是简单的使用该 关键字中的其中的一个作用:
- this. 修饰属性,可以在本类中访问自身属性
- ==this.==表示当前对象引用(注意不是当前对象). 可以借助 this 来访问对象的字段和方法.
- 要习惯用上this. 关键字,大大提高代码的可读性
class Person10 {
private String name;
public void setName(String name) {
name = name;
}
public String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
Person10 person10 = new Person10();
person10.setName("你好世界");
System.out.println(person10.getName());
}
}
- 结果:
- 上述代码:我们明明赋值了,可显示的结果确实 null (空),为什么?
- 我们来分析一下下面的代码:
public void setName(int name) {
name = name;
/*注意:问题出现在这里,我们不能对于当形参,与我们的属性名字相同时
,其意义就被改变了,name = name 这是在给自己赋值,name这里成了一个局部变量,局部变量优先原则,代表的就是 局部变量自己赋值给自己了,而局部变量,出了作用域,释放空间了,并没有达到我们属性赋值的效果,*/
/* 解决方案:
使用关键字 (this.)表示当前对象引用(注意不是当前对象),是引用,可以借助this.来访问对象的字段和方法,
*/
}
- 解决方法:
class Person10 {
private String name;
public void setName(String name) {
this.name = name; // 注意这里:(this.)表示引用当前对象,name 赋值的就不再是自身局部变量了,而是该对象中的属性;
}
public String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
Person10 person10 = new Person10();
person10.setName("你好世界");
System.out.println(person10.getName());
}
}
- 结果:
- 当在类中,我们的形参在命名上与类中的属性,发生冲突时,可是,我们一般希望我们在 setter 方法中的形参都是与 类的属性名是一致的,这样可以减少我们命名上的苦恼,命名一致可读性又非常高,但命名一致就会发生冲突(局部变量化),基于这么多的好处,我们可以使用this. 关键字,就可以避免这样的冲突问题。
- this. 关键字指代对象自身,表示引用当前自身对象
- 是不是非常 nice ,嘿嘿,这只是this. 关键字的冰山一角,后面我会一一讲清楚的。
最后:
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家!后会有期,江湖再见!