前言❤️❤️
hello hello💕,这里是洋不写bug~😄,欢迎大家点赞👍👍,关注😍😍,收藏🌹🌹
🎇个人主页:洋不写bug的博客
🎇所属专栏:Java学习之旅,从入门到进阶
🎇铁汁们对于Java的各种常用核心语法(不太常用的也有😆),都可以在上面的Java专栏学习,专栏正在持续更新中🐵🐵,有问题可以写在评论区或者私信我哦~
前序
(链接挂在下面了)。这部分知识有一点点抽象,那特别是我加粗的字体,大家耐心读几遍,应该还是能理解的差不多的,加油,奥里给!!!
1,封装
1,基础概念
笔记本电脑和手机,我们经常使用它,但是却不知道它内部的电子元件是如何工作的
类似的东西还有很多,像冰箱,汽车,电视等等
这种只使用,而不知道内部细节的东西就是封装。
封装的核心就是把细节隐藏起来,在Java中,封装有三个意思:
- 创建方法时,把方法实现的细节藏起来;
- 创建类,把类实现的细节藏起来;
- 实现网络协议,把下层协议藏起来。
现在重点要学的就是创建类,把类的细节藏起来,这就指的是类中的方法属性,只能在类内部使用,在外面感知不到。
概念:
private修饰的属性,方法,就是只能在类的内部使用,这就是封装。
public修饰的属性,方法,可以在类外部使用,这就不是封装。
写代码的时候,不能直接全部用private修饰(封装),当然也不能全都用public修饰(不封装),哪些属性方法要public/private,其实是取决是具体的场景的。
2,代码解释
光看概念可能有点抽象,在类和对象(一)的博客中,定义了一个学生类,用来演示类怎么定义,里面无脑的全部使用了public
这里把学生个人的隐私内容用private给封装起来,方法仍然去使用public
代码如下:
public class Student {
private int id;
private String name = "大明";
private int age;
private String gender;
private double score;
public void doHomework(){
System.out.println(name + "同学在做作业");
}
public void study(){
System.out.println("同学在学习");
}
}
在Java类和对象(三)的博客提到了用包来管理代码,这里在csdn这个包里再创建一个exercise类,用来进行测试

这个exercise就是相同包中的其他类,接下来在exercise类中创建一个学生对象,然后我们在分别看下是否能够调用方法和获取学生姓名。
public class exercise {
public static void main(String[] args) {
Student student = new Student();
student.study();
System.out.println(student.name);
}
}
这里方法是可以正常调用,但是学生的姓名却没办法获取,这是因为把name属性设置成了private

private数据在类外无法获取,在Student类内部是可以获取使用的
代码示例如下:
在Student类中的doHomework方法这里同样使用了name,这里不会爆红,
在类外调用该方法,也会正常打印出学生的名字

public class exercise {
public static void main(String[] args) {
Student student = new Student();
student.doHomework();
}
}

有的铁汁到这就会说:你用private来保护这些学生的个人信息在外面的类中不会被直接读取到,同时又在方法中加入学生的姓名,在类外面打印方法就又能知道学生姓名。那这样折腾图什么呢
其实是这样的,封装并不是表明把这些变量彻底封死,如果都彻底封死,那干脆就不用定义这些变量了
封装只是限制一下在类外读取这些数据的方式。
大家不要小看这个小小的限制,这里封装后只能通过一些方法来读取;如果不封装的话,那么这些变量在外面不仅能被调用,也能被胡乱修改
封装后,如下图所示,在类外面,如果尝试读取变量的话,根本读取不了,甚至类的使用者都不知道这个类中有什么变量

3,get和set方法
封装远远不只是用public和private这么简单,还可以用set,get方法来实现对类的封装。
例如在Student类中,想要放出gender(性别)的修改权限,但是又害怕调用者乱改,凡是gender,都是male/female
但是就是怕有的人乱改,这点在国外比较明显,比如把性别设定成沃尔玛购物袋,武装直升机什么的
那怎么限定调用者乱改呢,就需要写一个自定义的set方法,只需要让调用者输入自己到底是不是男的就行了,是男的输入true,是女的输入false,这样性别就非常正常了。
如果想让用户只读取数据,不修改数据,那就需要用到get方法,用户想查看这些信息时就使用get方法,只有一个读取的作用
public class Student {
private int id;
private String name = "大明";
private int age;
private String gender;
private double score;
public void doHomework(){
System.out.println( name + "同学在做作业");
}
public void setGender(boolean isMale){
if(isMale){
gender = "male";
}else{
gender = "female";
}
}
public String getGender(){
return gender;
}
public void study(){
System.out.println("同学在学习");
}
}
public class exercise {
public static void main(String[] args) {
Student student = new Student();
student.setGender(true);
System.out.println(student.getGender());
}
}

4,get和set方法的自动生成
在类中按alt + insert键,就可以自动生成这些方法,Getter and Setter选项的意思就是同时生成get和set方法

选中age,点击ok,就可以生成age的get和set方法
在自动生成后也可以根据自己的需要对进行修改
铁汁们熟练get和set方法后,就不再写了,都是自动生成的


自动生成时,可以按住ctrl键,用鼠标点击选中多个成员变量,同时生成多个成员变量的get和set方法,这样就非常高效

2,static关键字
1,发展演化过程(有兴趣看)
这个static关键字其实在最早C语言的时候就已经有了,那时候主要有以下三个用途:
①:修饰局部变量,把变量的生命周期延长,跟随整个程序
在这个func函数中,如果正常不写static的话,每次调用完方法都会销毁count变量,下次调用时count的值就是0
写了static,方法执行完count值不会销毁,只有整个程序都运行结束count才会销毁
那执行过一次func方法,下次再执行,count的初始值就是1了,不是0
void func() {
static int count = 0;
count++;
printf("count: %d\n", count);
}
下面这句代码是只运行一次的,因为每次出方法count都不会被销毁,也就不需要再初始化count
static int count = 0;
②:修饰全局变量,把变量的作用域限制到到当前的.c文件中,不能在其他的源文件中使用,因此,在其他的源文件中也可以定义名字相同的变量。
③:修饰函数,也是把函数的作用域限制到了当前的.c文件中去,跟限制全局变量的效果类似。
最初在C语言中static就是这几个意思,看完后就会发现这跟Java中的static用法不沾边,那在演化过程中为什么有如此巨大的差异呢?那就要从头说起了
在美国有一个超级牛逼的实验室,叫贝尔实验室,像太阳能电池,通信卫星,电子数字计算机,都是贝尔实验室发明的,而贝尔就是最早发明电话的那个人,这个实验室有一多半的人都获得过诺贝尔奖,C语言跟C++的发明人就都是这个实验室的。

后来,在创建C++时,需要一个词来表示这样的概念,那就是类属性/类方法(稍后博客会重点去说),这时候重新引入一个新的关键字风险是有点高的
因为写C语言跟C++的软件,其实是通用的,引入一个新的关键字可能会破坏兼容性
因此,创建C++的大佬就想着把C语言中的static搞过来,用一下,就算原来的定义跟现在没啥关系
Java是在C++之后创建的,C++创建类的对象的基本思路跟Java是相似的,因此创建Java的大佬就把这个C++中这个static概念搞了过来,直接就用了。
2,类属性/类方法
类属性和普通属性:
如下图,在Student中定义的这些变量没有加staic,都是普通属性
普通属性在创建对象的时候会被初始化,调用是通过创建的对象后面加上点来调用,也就是通过对象来调用的。

类属性就是在定义变量前加入了static,类属性跟普通属性在初始化和调用上都是有所不同的
- 类属性是属于类(加static),普通的属性(不加static)就属于对象,类属性其实比普通属性更牛的,高一个级别,类属性在类加载时就能初始化,而普通属性只有在创建对象时才会被初始化。
- 类属性因为是属于类才,所以在调用时就是通过类名来调用(用对象调用也可以,但还是建议通过类名调用),而实例属性只能通过对象来调用
我们可以在Student类中写一个main方法测试一下,我们会发现,加上static的属性都可以用类名来调用。


再创建一个对象,试试能不能调用刚刚加static的那三个变量,这里是不行的

类方法和普通方法的解析:类方法和普通方法主要就是调用的区别,因为方法是不用初始化的,因此就不存在初始化的区别,类方法是通过类来调用,对象调用不了;普通方法就是对象调用的。
在Student类中写一个study方法,前面加上static

在Student类中的main方法中试一下,无需创建对象,就可以用类名来调用(如下图)
可以再创建一个对象试一下,看对象能不能强制调用static方法
再把static删了,再看看类能不能调用,跟前面的类属性/实例属性效果相似
3,深度解析(代码)
1,调用补充
下面是用对象强制调用类属性,这里并不建议这样做,还是建议用类去调用,真是强制用对象调用也不会报错,示例如下:
package csdn; public class Student { private static int id; private static String name; private static int age; private String gender; private double score; public void doHomework(){ System.out.println( name + "同学在做作业"); } public void setGender(boolean isMale){ if(isMale){ gender = "male"; }else{ gender = "female"; } } public String getGender(){ return gender; } public static void study(){ System.out.println("同学在学习"); } public static void main(String[] args) { Student student = new Student(); System.out.println(); } }选项框里并没有类属性

自己打出来,也不会报错,只是会有个警告。

2,类属性属于整个类(内存)
类属性是属于整个类的,比如我们在一个类中创建三个对象,那么像gender,score这些普通属性就会有三份,但是id,String,age都是只有一份,因为它们是类属性,是被所有对象所共有的

使用代码来验证一下,这里为了方便验证,就用对象来调用这个类属性,日常写代码不建议这样做public class Test { public int n = 10; public static int m = 20; public static void main(String[] args) { Test t1 = new Test(); t1.n = 100; t1.m = 200; Test t2 = new Test(); t2.n = 300; t2.m = 600; System.out.println("t1.n = " + t1.n); System.out.println("t1.m = " + t1.m); System.out.println("t2.n = " + t2.n); System.out.println("t2.m = " + t2.m); } }通过打印结果就可以看出,这个类属性在类中是只有一份的,因此,是以最后修改的为准

n为成员属性(变量),只要是new出来的东西,都是储存在堆上的,new出来的Test对象t1,t2就是储存在堆中,n随着对象储存在堆中;因此,创建几个对象,n就有几份。
m是类属性,它总共只有一份,就储存在方法区中()。
在数组基础博客的第5部分,是Java相关的内存知识,对内存还不太清楚的铁汁们可以看一下,下面这张图片在那篇博客上截取的,方法区就是内存区域的一部分
方法区不同于堆和栈空间,它是存储整体类的信息,下面图中整个大长方形就是方法区,里面的小格子是他存储的信息

静态成员变量(类属性,不加static直接说是成员变量)的特征:
1,不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中。
2,既可以通过对象访问,也可以通过类名访问,但是建议通过对象访问。
3,静态成员变量存储在方法区中
4,生命周期伴随类的一生(从程序启动开始,到程序结束为止)3,类方法
static修饰的方法就是类方法,类方法跟类有关,跟对象(实例)无关
static修饰属性,决定对象存储的位置,而static修饰方法,是不影响方法存储的位置,因为方法都是储存在方法区中的。- 不加static就是实例方法,就得通过实例名.方法名来调用,此时方法内部也可以使用实例属性(也就是成员变量,不加static定义的)和其他的实例方法(不加static定义的方法)
- 加了static就是类方法,就得通过类名.方法名来调用,对象.方法名也可以,但是不推荐,示例如下:
public class Test { public int n = 10; public static int m = 20; //普通的实例方法 //可以使用实例属性,也可以使用类属性 public void func1(){ System.out.println(n); System.out.println(m); } //类方法 //只能使用类属性,不能使用实例属性 public static void func2(){ System.out.println(n); System.out.println(m); } public static void main(String[] args) { } }在类方法中只能使用类属性,不能使用实例属性,这里在类方法中调用实例属性(成员变量),就会爆红,显示调用不了。

总结就是:实例方法中可以使用实例属性,也可以使用类属性。而类方法中只能使用类属性,不能使用实例属性
接下来再看下方法的调用规则
public class Test { public int n = 10; public static int m = 20; //普通的实例方法 //可以使用实例属性,也可以使用类属性 public void func1(){ System.out.println(n); System.out.println(m); } public void func2(){ func1(); func3(); func4(); } public static void func3(){ func4(); func1(); } public static void func4(){ } public static void main(String[] args) { } }这里func2(实例方法)对于类方法和实例方法都可以调用,但是func3(类方法)就只能调用类方法了,尝试去调用实例方法就会报错。

在对实例方法的调用这里,其实本质上是有个this的,只是写的时候省略了,this指的是通过当前调用方法的对象来调用该方法

这个到底哪个可以调用哪个,有个记忆技巧
只需要记住static修饰的方法里面,是没有默认this的,this就是用来调用实例方法的
也就是在static修饰的方法(类方法)里面不能使用实例方法和实例属性我们看下这段代码,在类方法中调用n这个实例属性就会报错
public class Test { public int n = 10; public static int m = 20; public static void main(String[] args) { System.out.println(n); System.out.println(m); } }报错的英文翻译过来就是无法从静态(加了static)上下文中引用非静态(不加static)字段

- 补充:这里的main方法也是一个类方法,调用时也是通过类名.main来调用的,只不过这个调用过程不用我们手写,是JVM自动完成的
3,代码块
一个代码块就是一个{ }
一个类也是可以通过{ }进行初始化的。1,普通代码块
定义方法{ }
if{ }
while{ }
for{ }
这些都是普通代码块。2,构造代码块/静态代码块
构造代码块也叫做实例代码块,就是定义在类中的代码块,不加修饰符,构造代码块一般用于初始化成员变量。
里面写的内容是对成员的初始化操作,执行时机就是在new对象的时候
构造代码块是不用static修饰的,里面是有this的,可以访问类属性/类方法,也可以访问实例属性/实例方法。使用static定义的代码块称为静态代码块,一般用于初始化静态成员变量。
执行的时机就是在类加载的时候,一般只执行一次,在 Java 中,静态代码块一般是类中最早执行的代码
类就好比是一个房子的图纸,而对象就是根据这个图纸去建造房子,静态代码块相当于在图纸的研发阶段就执行了。下面这段代码的main方法是空的,但是只要加载了这个类,就会运行这个构造代码块:
public class Test { public int n = 10; public static int m = 20; //构造代码块 { n = 20; m = 10; System.out.println("执行构造代码块"); } static{ m = 30; System.out.println("执行静态代码块"); } public static void main(String[] args) { } }
在main函数里面创建两个对象,看运行结果就知道,静态代码块只随着类的加载执行一次,而构造代码块每创建一次对象,都会执行一次:
public static void main(String[] args) { Test t1 = new Test(); Test t2 = new Test(); }
注:静态代码块是大括号前面只有一个static的
像main方法,还有那些用static修饰的静态方法,都不是静态代码块
方法不属于任何代码块,代码块大括号前面只有什么都不写或者只能有一个关键字(比如if while static)这只是有这样一个概念,在平常写代码时,基本上不用静态代码块
静态代码块主要就是初始化静态属性,在定义这个静态属性的时候一般可以直接初始化,也就是就地初始化,再去写个代码块去初始化就有点太麻烦了😅
4,OJ题
注:这种OJ题面试时是检查切窗口的,在面试时我们如果切到IDEA环境下来写,就会被检测出作弊。
1,修改Data类的定义


这里的构造方法和get方法都是私有的,在main方法中无法访问,我们要把它改为public;另外,在构造方法中,实参的名字跟成员变量名字相同,编译器无法区分,要加上this

2,验证年龄
这也是一个非常基础的题目,就是让我们补全一下类,再加上一个判断逻辑:

5,概念题(知识补充)
public static void main(String[] args){ String s; System.out.println("s="+s); }这段代码的运行结果是什么???
A.代码编程成功,并输出”s=”
B.代码编译成功,并输出”s=null”
C.由于String s没有初始化,代码不能编译通过。
D.代码编译成功,但捕获到NullPointException异常这里选C,因为这里的s是局部变量,局部变量必须初始化,才能打印,否则编译都通过不了
阅读如下代码。 请问,对语句行 test.hello(). 描述正确的有()
package NowCoder; class Test { public static void hello() { System.out.println("hello"); } } public class MyApplication { public static void main(String[] args) { // TODO Auto-generated method stub Test test=null; test.hello(); } }A.能编译通过,并正确运行
B.因为使用了未初始化的变量,所以不能编译通过
C.以错误的方式访问了静态方法
D.能编译通过,但因变量为null,不能正常运行这道题大家应该一看A这个选项,就直接排除了,但是这道题就是选A。
那创建个空的对象也能调用方法吗
我们前面在说static的时候提到,static方法其实是属于类的,因此建议用类去调用
如果强制用对象去调用也不会报错(这个方法实际上跟对象是没关系的),这里编译器在运行的时候,会把test.hello();这句转为Test.hello();不看对象,因此对象为null也能正常运行这段代码的运行结果是什么
public class Pvf{ static boolean Paddy; public static void main(String args[]){ System.out.println(Paddy); } }答案是编译通过并输出false,
成员变量在创建时会有默认的初始值,数字类型默认值是0,boolean是false,像String这些引用类型默认是null结语💕💕
这些部分就是属于类和对象中相对比较进阶的,铁汁们多练代码,一定可以掌握的👍👍👍
以上就是今天所有的内容啦~ 大家都学废了吗?完结撒花~ 🥳🎉🎉



1150

被折叠的 条评论
为什么被折叠?



