🎇🎇🎇作者:
@小鱼不会骑车
🎆🎆🎆专栏:
《java练级之旅》
🎓🎓🎓个人简介:
一名专科大一在读的小比特,努力学习编程是我唯一的出路😎😎😎
🙈🙈🙈作者心里话
小鱼一直都是秉承着“开开心心看博客,快快乐乐学知识的观点,虽然浏览量好低…”
前言
继上次博客,这次讲到的是类和对象的第三部分
面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说
就是套壳屏蔽细节。
生活案例:
生活中的封装比如ATM收款机,会使用一个机器来保证钱的正常流动,给你一个特定的方法来存钱取钱,不会说让你随意的取钱存钱,在java中,封装也是这个概念,封装就是把过程和数据包裹起来,对数据的访问只能通过已经定义的接口,面向对象计算始于这个基本概念,即现实世界已经被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象
java中封装的理解:
我们在程序设计的时候就追求“高内聚、低耦合”
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
- 低耦合:仅对外部暴露少量的方法用于使用。
隐藏对象内部的复杂性、只对外公开简单的接口。便于外界调用从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装的设计思想。
- 在Java中通过权限修饰符关键字private、protected和public实现封装
java中的四个访问修饰限定符
简单介绍一下这四个修饰限定符,后期会一一用到
default权限其实可以理解为默认权限,就是自己这其他三个权限都不设置的情况下,我们的编译器就会默认为default权限,例如
class Person {
private String name;//名字
public int age;//年龄
int height;//体重,默认为default
}
其中我们的height就是default修饰的权限
注意:一般情况下成员变量设置为private,成员方法设置为public
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件
包。有点类似于目录。比如我们为了更好的管理学习到的知识点,我们就可以用一个一个包,一种好的方式就是将相同属性的知识点放在相同文件下,也可以对某个文件夹下的知识点进行更详细的分装。
Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.
public class Test1 {
public static void main(String[] args) {
java.util.Date date=new java.util.Date();
//但是大家看上面代码是不是有一些冗余
System.out.println(date);
}
更简便的方法:可以使用 import语句导入包.
import java.util.Date;//用import导入这个util.Date这个包
public class Test1 {
public static void main(String[] args) {
Date date=new Date();
//上述代码,代码量减少但是作用没变
System.out.println(date);
}
}
当然,如果我们需要util包中更多的类,我们就可以用import java.util.*
import java.util.*;//用import导入这个util.Date这个包中所有的类
public class Test1 {
public static void main(String[] args) {
Date date=new Date();
//上述代码,代码量减少但是作用没变
System.out.println(date);
}
}
虽然我们这个是可以导入这个包中的任意类,就是我们用什么,他就默认导入什么类,但是不要和C语言的#include弄混了,C语言在预处理的时候将头文件中所有的代码都添加到了后缀为.i文件中,
但是java不一样,它并不会直接将那些包中的代码全部引入,只会是选择自己使用到的类进行引入。
但是需要注意的是java.sql 中的类 java.sql.Date 和 java.util 中的类 java.util.Date 都匹配
那么就会报错,因为编译器是无法识别我的这个Date是哪个包里的,如果想解决这个问题,我们就只能使用完整的类名,这样就会解决编译器识别不清楚的问题。
import java.util.*;
import java.sql.*;
public class Test1 {
public static void main(String[] args) {
java.util.Date date=new java.util.Date();
System.out.println(date);
}
}
总结:
import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要,import 只是为了写代码的时候更方便.,import 更类似于 C++ 的 namespace 和 using
基本规则
如何创建一个包
第一步.
Test.java
,在文件的最上面出现了一个 package 语句Computer在我们的com.baidu.www
的包里,Person在我们的com.Google.www
的包里,两者不在同一个包
Computer类
package com.baidu.www;
public class Computer {
private String cpu; // cpu
String memory; // 内存
public String screen; //屏幕
public void Boot() {
System.out.println("开机");
}
public Computer(String cpu, String memory, String screen) {
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
}
}
Person类
package com.Google.www;
import com.baidu.www.Computer;
public class Person {
private String name;//姓名
private int age;//年龄
public void sleep() {
System.out.println(name+"睡觉");
}
public void eat () {
System.out.println(name+"吃饭");
}
public static void main(String[] args) {
Computer computer=new Computer("HW", "i7", "8G");
System.out.println(computer.cup);
System.out.println(computer.memory);
//报错,cpu是私有的是private修饰的,
//memory是default修饰的,是只能在自己的包中使用的
}
}
// 注意:如果去掉Computer类之前的public修饰符,代码也会编译失败!!!
这部分是本节的重点
我们创建一个学生类
public class Student {
private String name;//姓名
private int age;//年龄
private String sex;//性别
private String classroom;//教室
public void sleep () {
System.out.println(name+"上课");
}
public void eat () {
System.out.println(name+"吃饭");
}
public void print() {
System.out.println(name+" "+age+" "+sex+" "+classroom);
}
public Student(String name, int age, String sex, String classroom) {
this.name = name;
this.age = age;
this.sex = sex;
this.classroom = classroom;
}
public static void main(String[] args) {
Student s1=new Student("小明",18,"男","高三四班");
Student s3=new Student("小王",17,"男","高三四班");
Student s2=new Student("小张",19,"女","高三四班");
s1.print();
s2.print();
s3.print();
}
}
大家看,我们的教室是不是都一样,但是每次我们还需要再去填入是哪个教室,是不是很麻烦,并且大家也可以看到我们的变量都是开辟在堆上的
这三个对象里面都有相同的教室,是不是我们可以把这个教室单独拿出来,让这三个类都可以共享到,这个时候就可以使用static这个关键字了
被static修饰的成员变量是静态成员变量,不属于某个对象,是所有对象共享的。
那我们该如何单独访问这个静态成员变量呢?
试试用对象的引用访问
我们会发现,在点之后并没有我们想要的classroom,这是因为我们的classroom是静态成员变量,不会出现在引用中,但是如果强行去调用也是可以的
更标准一些的写法就是用类名去调用
其实我们的静态成员数据是不占内存的(随类的加载而加载),我们可以不用实例化就去调用
public static void main(String[] args) {
// Student s1=new Student("小明",18,"男");
System.out.println(Student.classroom);
//没有实例化依然可以调用
}
同时我们还可以发现,我们的静态变量是不会储存在某个具体的对象中的
最后我们就可以总结静态成员变量的特性
只要被static修饰的变量就不属于对象
还有一个重要的知识点就是,我们去修改静态成员变量的值,但是每次实例化新对象后,接受的静态成员变量的值也都是被修改过的
class P{
public static int a=0;
}
public class Test2 {
public static void main(String[] args) {
P p1=new P();
P p2=new P();
P p3=new P();
p1.a+=5;
p2.a+=4;
p3.a+=2;
p1.a-=8;
System.out.println(P.a);
}
//最后输出3
所以我们就把这个成员变量的权限设置为private以防被外界篡改信息
}
一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中classRoom属性如何
在类外访问呢?
public class Student {
private String name;//姓名
private int age;//年龄
private String sex;//性别
private static String classroom="高三四班";//教室
public void sleep () {
System.out.println(name+"上课");
}
public void eat () {
System.out.println(name+"吃饭");
}
public void print() {
System.out.println(name+" "+age+" "+sex+" "+classroom);
}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
class Main {
public static void main1(String[] args) {
System.out.println(Student.classroom);
}
这里会报错,因为我的classroom的权限仅限于在类里面
}
那我们该如何访问呢?这时候就需要一个一个接口,注意!!!这个接口最好是被static修饰的,下面给大家解释
我们可以通过这个快速创建两个方法,用来修改值和返回值的
public class Student {
private String name;//姓名
private int age;//年龄
private String sex;//性别
private static String classroom;//教室
public static String getClassroom() {
return classroom;
}
public static void setClassroom(String classroom) {
Student.classroom = classroom;
}
}
class Main {
public static void main(String[] args) {
Student.setClassroom("高三四班");
System.out.println(Student.getClassroom());
}
// 最后输出高三四班
}
前面讲了,我们的静态变量是不需要实例化对象的,可以直接使用,虽然我们无法直接修改classroom的值,但是我们可以通过输入接口来进行修改,但是大家有没有注意到,我们的这两个接口的方法都是被static修饰的。如果用的是非静态的方法,那我们还要再去实例化一个对象
public class Student {
private String name;//姓名
private int age;//年龄
private String sex;//性别
private static String classroom;//教室
//没有用static修饰
public String getClassroom() {
return classroom;
}
//没有用static修饰
public void setClassroom(String classroom) {
Student.classroom = classroom;
}
}
class Main {
public static void main(String[] args) {
Student student=new Student();//需要实例化对象才可以使用
student.setClassroom("高三四班");
System.out.println(student.getClassroom());
}
}
那我们可以在静态方法中用静态成员变量,那我们可不可以用非静态成员变量呢?或者非静态的成员方法呢?
public class Student {
private String name;//姓名
private int age;//年龄
private String sex;//性别
private static String classroom;//教室
public static String getClassroom() {
return classroom;
}
public static void setClassroom(String classroom) {
Student.classroom = classroom;
System.out.println(name);
System.out.println(age);
}
}
class Main {
public static void main(String[] args) {
Student.setClassroom("高三四班");
}
//报错,无法从静态方法中引用非静态的成员数据
}
静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中无法传递this引用。
因为非静态成员的是属于对象的,我在调用静态方法时没有实例化任何对象,所以如果想访问非静态数据成员那就要实例化这个对象,用这个对象的引用去调用,就像我们的main函数,是静态方法,想在静态方法里面使用非静态的成员数据就需要实例化对象
public static void main(String[] args) {
Student.setClassroom("高三四班");
Student student=new Student();
//用这个引用去调用这个非静态对象
//非静态成员变量。刚才已经修改了age权限所以现在可以用
student.age=10;
//非静态成员方法
student.print();
}
这样就会避免报错
但是普通成员方法中可以调用静态方法!!!
总结静态方法
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
就地初始化已经讲过了
private static String classroom="高三四班";//教室
那什么是静态代码块初识化呢?
使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块:定义在方法中的代码块
public class Test4 {
public static void main(String[] args) {
{
System.out.println("我是个普通代码块");
}
System.out.println("========我是分割符");
}
最后输出
我是个普通代码块
我是分隔符
}
public static void main(String[] args) {
System.out.println("========");
{
System.out.println("我是普通代码块");
}
}
最后输出
我是分隔符
我是普通代码块
我们就可以发现我们的普通代码块是根据在方法中的顺序,从上往下执行的。当然,如果我们在普通代码块中创建局部变量,那么出了这个代码块,这个局部变量就会销毁
用法比较少见
依旧是给大家对比一下我的代码块放在不同的位置输出的结果
由此我们可以总结到,我们的实例/构造代码块是在调用构造方法之后优先执行的, 一般就用于初始化实例化成员
一般用于初始化静态成员变量。
什么是静态代码块呢?就是用static修饰的代码块,我们接下来进行两组对比
对比一
静态代码块放在构造方法的前面和后面
对比二
静态代码块和实例代码块进行对比,分别按不同的顺序
总结: 静态代码块优先于构造方法执行,并且静态代码块根据先后顺序来绝对谁先执行
总结: 不论先后顺序,静态代码块一定是在构造方法中第一个执行的,执行完静态代码块再去执行实例代码块,最后执行构造方法。