目录
? ? ? ?我们知道面向对象语言有三大特征:封装,继承,多态。在此之前我们也知道了什么是类和对象(一定要有这些基础知识,详情请看Java中的类和方法(方法重载)-CSDN博客)。这一篇我们就来详细讲解封装和继承。
? ? ? ?听名字,像是包装。其实它就是包装,封装简单来说,就是套壳屏蔽细节,也就是包装。比如电脑我们只能看到主机,但是主机里面还有很多核心部件,比如主板,显卡等等。
? ? ? ?但使用者不必注意细节,只用知道如何去使用即可。这也就是封装的概念。在此之前,我们还需要一些预备知识。
? ? ? ?为了更好的管理类,我们提出了一个包的概念,把多个类收集在一组,称为软件包。
? ? ? ?包其实就是文件夹下的一个类。在同一个工程中,允许存在相同名称的类,只要处在不同的包中即可。我们平时输入要用到Scanner这个类,用到这个类需要导包。
? ? ? ? 比如说我们此时要用的date有关于日期的类,如果我们不导包,就只能这样用:
import java.util.Date;
public class test2 {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();//不导包只能这样指定使用
Date data1 = new Date();//导入包以后就可以这样使用
}
}
? ? ? ?当我们想导入包时,我们可以双击shift,并点击class,输入查找的包,可以查看具体源码实现。
? ? ? ? 我们也可以使用通配符:
? ? ? ?但是一般不建议这样写,我们只需要导入具体的包,这样更加精准。
? ? ? ?我们也可以只导入包中的静态方法。
//import static java.lang.Math;//导入Math包中的所有静态方法不能这样写
import static java.lang.Math.*;//只能这样写
? ? ? ?其实导入静态方法是有好处的,它可以方便类中方法的使用,比如我们已经导入了Math中的所有的静态方法,此时我们就可以这样写:
//import static java.lang.Math;//导入Math包中的所有静态方法不能这样写
import static java.lang.Math.*;//只能这样写
public class test2 {
public static void main(String[] args) {
double x = 30;
double y = 40;
//静态导入的方式写起来更方便
//double ret = Math.sqrt(Math.pow(x,2) + Math.pow(y, 2));
double ret = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(ret);
}
}
? ? ? ?导入静态方法会更加方便一些。?和C/C++引用头文件不一样,他们引入头文件其实就是把它所有的内容都拷贝过来;而Java就是要到哪一个才会拿哪一个。
? ? ? ?我们可以自己定义包,右击src,新建package(全部使用小写,一般使用公司域名颠倒形式)。之后往包里面创建类。
? ? ? ?所以包名要与代码路径相匹配,例gan.www的包,就会存在一个对应路径gan/www来存储代码。如果没有包(package)语句,则该类被放在一个默认的包中。
? ? ? ?还记不记得我们之前每次都在使用public修饰,绝大多是是用public修饰的,少数是用private。一共分为3类。
? ? ? ? 我们要知道封装,就一定要知道这些访问修饰限定符的含义。
? ? ? ?将属性修改为私有属性,这个属性只能当前包当前类中才能访问。
? ? ? ?我们将其修改为private,之后发现赋值就出现错误了。?此时在类外就看不到name,这就体现了它的封装性。
? ? ? ?此时只能这样赋值,因为不知道其具体属性。
?? ? ? ?我们也可以给方法限定为private方法,这样类外也拿不到。比如我们将构造方法修改为private修饰。
?? ? ? ?此时只能在当前类中使用。?此时如果我们想使用不带参数的构造方法该如何呢?很简单,在类中设置方法。
? ? ? ?此时我们只能通过提供公开的get和set方法,帮助我们获取或者修改成员。
? ? ? ?当我们都用private修饰时,只能通过内部提供的公开方法才可以拿到属性和赋值属性时,这就叫做封装。
public class People{
private String name;
private int age;
public People(){
System.out.println("不带参数的构造方法");
}
public People(String name, int age) {
this.name = name;
this.age = age;
System.out.println("带参数的构造方法");
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void sleep(){
System.out.println(this.name + "正在睡觉");
}
}
public class test2 {
public static void main(String[] args) {
//People person = new People("脏三",9);
//此时只能这样赋值
//无参构造需要类中有方法
People people = new People();
people.setName("脏三");
System.out.println(people.getName());
}
}
?? ? ? ?此时如果都是私有属性,难道我们要一个一个set和get吗?IDEA其实也有快捷键,右击点击generate,并点击Setter and Getter之后全选即可。
? ? ? ?好的,目前我们已经知道如何使用private和它的性质了,那么我们接下来就要知道其他的限定修饰访问符的用法和性质了。
? ? ? ?这里大家会问,default是个啥,也没见过。对,确实没见过,但是我们前面不给任何修饰就是默认(default)的。比如此时我们这样定义成员属性:
private int age;
int r;
? ? ? ? 其实也就是翻译的问题而已。我们已经知道包是什么了,接下来我们来演示default的权限。
? ? ? ?protected我们等学完继承以后再讲。
? ? ? ?接下来我们讲解static修饰符的使用方法。
public class Student {
public String name;
public int id;
public String className;
public Student(String name, int id, String className) {
this.name = name;
this.id = id;
this.className = className;
}
}
public class Main {
public static void main(String[] args) {
Student student1 = new Student("zangsan",10,"301");
Student student2 = new Student("Lisi",11,"301");
Student student3 = new Student("wangwu",13,"301");
}
}
? ? ? ?我们创建三个学生变量,他们都在同一个班级上课。既然如此就会显得有些冗余,占据多余空间,此时我们可以将其班级成员变量设置为静态变量。
? ? ? ?此时我们再右击生成形参构造器时也就看不到能构造静态成员变量的选项了。?? ? ? ?此时我们先将其className静态成员就地初始化为301。并逐个给每个学生赋值班级。
public static String className = "301";
?? ? ??在赋值期间,我们发现.不出来className必须全部拼写出来,而且静态成员也不是在对象中存储的,所以可以见得,static静态成员变量并不是这样用的。?因为不是对象中的成员,所以我们直接通过类名调用。
//通过类名访问
System.out.println(Student.className);
?? ? ??同理,也有静态方法,也是通过调用类名来使用。?
? ? ? ?所以,static修饰的成员变量称为静态成员变量。特征:不属于某具体的对象,是所有对象所共享的。?既可以通过对象访问,也可以通过类名访问(但推荐使用类名访问)。生命周期伴随类的一生。
? ? ? ?静态成员变量存在方法区当中。
? ? ? ?静态方法中不能调用 非静态的成员变量 或者 非静态的成员方法。
? ? ? ?所以静态方法不包含this。
? ? ? ?静态方法无法重写。所以有些地方这样写:
? ? ? ?代码块我们都知道是{},平时用的就是普通代码块,但是在Java中,还有分类:
- 普通代码块(这里不再赘述)
- 构造块
- 静态块
- 同步代码块(这个后续了解)
? ? ? ?这里我们一般使用静态代码块初始化静态成员变量。为了更好举例,我们再给出构造代码块的应用。
? ? ? ?此时我们发现先执行了静态代码块,为了更好的总结规律,此时我们在实例化一个对象:
? ? ? ?此时可以发现静态代码块只执行一次。若一个类中有多个静态代码块,其实相当于合并执行了。
public class Student {
public String name;
public int id;
public static String className;
public static int i;
//无参构造器
public Student(){
}
//构造代码块
{
i++;
}
public Student(String name, int id) {
this.name = name;
this.id = id;
//System.out.println("hahaha");
}
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student();
Student.i++;//i = 2
System.out.println(Student.i++);//i = 3
System.out.println(Student.i++);//i = 4
System.out.println(Student.i += 3);//i = 7
Student s2 = new Student();
//执行了构造代码 i++
//因为静态代码只执行一次
System.out.println(Student.i);//i = 8
}
}
? ? ? ?static修饰的常量和C语言一样,会延长变量的生命周期。因为并不是在堆区创建的,所以生命周期不会结束。
class Test{
public String toString() {
System.out.print("aaa");
return "bbb";
}
}
public static void main(String[] args) {
Test test = new Test();
System.out.println(test);
}
public class Again {
String s;
public static void main(String[] args) {
System.out.println(s);
}
}
? ? ? ?在同一个类中,静态方法无法调用非静态方法或者变量。除非在静态方法中实例化了一个对象。
public class Again {
String s;
public static void main(String[] args) {
Again a = new Again();
System.out.println(a.s);
}
}
public class Test {?
? ? public int aMethod(){
? ? ? ? static int i = 0;
? ? ? ? i++;?
? ? ? ? return i;
? ? }?
public static void main(String args[]){
? ? Test test = new Test();?
? ? test.aMethod();?
? ? int j = test.aMethod();
? ? System.out.println(j);
? ? }?
}
? ? ? ?这个编译出错,因为被static修饰的只能是成员变量,不能是局部变量。static的代码块是从上到下一次执行的。