第2节 面向对象进阶
一、封装与private
概述:
封装的意义在于保护或者防止代码(数据)被我们无意中破坏。保护成员属性,不让类以外的程序直接访问和修改。
封装原则:
隐藏对象的属性和实现细节,仅对外公开访问方法,并且控制访问级别。
我们观察如下代码:
class Person {private String name; //表示姓名private int age; //表示年龄void tell() {System.out.println("姓名:"+name+",年龄:"+age);}
}public class Demo {public static void main(String args[]) {Person per = new Person();per.name = "张三";per.age = -30;per.tell();}
}以上的操作代码并没有出现语法错误,但是出现了逻辑错误(年龄)。在开发中,为了避免出现逻辑错误,建议对所有属性进行封装,
并为其提供setter和getter方法进行设置和取得操作。修改代码如下:
class Person {private String name;private int age;void tell() {System.out.println("姓名:" + getName() + ",年龄:" + getAge());}public void setName(String str) {name = str;}public void setAge(int a) {if(a>0&&a<150) {age = a;}}public String getName() {return name;}public int getAge() {return age;}
}
public class OODemo10 {public static void main(String args[]) {Person per = new Person();per.setName("张三");per.setAge(-30);per.tell();}
}这样,当输入一个不符合逻辑的年龄时,不会对其赋值,所以保护了成员属性。
二、this关键字
在Java基础中,this关键字是一个最重要的概念,使用this关键字可以完成以下的操作:
-
调用类中的属性
-
调用类中的方法或构造方法
- 在一个构造方法中,调用另一个构造方法时,调用的代码必须编写在构造方法的第一行。
- 表示当前对象
package com.kaikeba.objectoriented.advanced;public class thiskeyword {public static void main(String[] args) {Person3 p1 = new Person3("张三", 18);Person3 p2 = new Person3("李四", 18);p1.say();p2.say();Person3 p3 = new Person3();p3.say();}
}/*** this指的是当前对象*/
class Person3 {private String name;private int age;Person3() {//无参构造方法内调用了全参构造方法this("默认姓名", 1);}Person3(String name, int age) {//this表示当前对象this.name = name;this.age = age;}void say() {System.out.println("姓名:" + this.name + ",年龄:" + this.age);}
}
结果如下:
姓名:张三,年龄:18
姓名:李四,年龄:18
姓名:默认姓名,年龄:1
三、static关键字
概述:
static表示“静态”的意思,可以用来修饰成员变量和成员方法 (后续还会学习 静态代码块 和 静态内部类 )。
static的主要作用在于创建独立于具体对象的域变量或者方法。
简单理解:
被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。并且不会因为对象的多次创建而在内存中建立多份数据。
观察如下代码:
package com.kaikeba.objectoriented.advanced;public class statickeyword {public static void main(String[] args) {
// Emp e1 = new Emp("张三", "北京");
// Emp e2 = new Emp("李四", "北京");
// Emp e3 = new Emp("王五", "北京");
// Emp e4 = new Emp("赵六", "北京");Emp.region = "北京";Emp e1 = new Emp("张三");Emp e2 = new Emp("李四");Emp e3 = new Emp("王五");Emp e4 = new Emp("赵六");e1.say();e2.say();e3.say();e4.say();
// //假设:公司迁址到 天津
// e1.setRegion("天津");
// e2.setRegion("天津");
// e3.setRegion("天津");
// e4.setRegion("天津");Emp.region = "天津";e1.say();e2.say();e3.say();e4.say();}
}class Emp {//不带static的可以理解为对象的属性,带static的可以理解为类的属性private String name;static String region; //static修饰的属性存在方法区中Emp(String name, String region) {this.name = name;this.region = region;}Emp(String name) {this.name = name;}Emp(){}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getRegion() {return region;}public void setRegion(String region) {this.region = region;}void say() {System.out.println("员工姓名:"+name+",员工所在地区:"+region);}}
其内存的存储如下所示,static修饰的region属性或方法,被存储在方法区中,类被加载的时候,就已经在方法区中开辟,且只会开辟一次,一般是通过类名来设置或修改,通过对象名也可以,但是它会把所有对象的这个属性都改了。
重点:
-
静态成员 在类加载时加载并初始化;
-
无论一个类存在多少个对象,静态的属性,永远在内存中只有一份(可以理解为所有对象公用);
-
在访问时:静态不能访问非静态,非静态可以访问静态 ,因为静态比非静态先创建,如下面的代码所示:
package com.kaikeba.objectoriented.advanced;public class statickeyword3 {public static void main(String[] args) {StaticDemo.say();}
}class StaticDemo {/*** 通过类名来调用,对象名也行,因为创建对象要先加载类* 静态修饰的方法,被调用时,有可能对象还未创建*/static void say() {System.out.println("勇敢牛牛,不怕困难");}/*** 通过对象来调用* 非静态方法可以调用静态方法,反之不行(先后创建顺序)*/void say2() {System.out.println("锄禾日当午");say();}
}
四、4种代码块
-
普通代码块 :在执行的流程中 出现的 代码块。
-
构造代码块 :在类中的成员代码块,每次对象创建时执行,执行在构造方法之前。
-
静态代码块 :在类中使用static修饰的成员代码块,在类加载时执行。每次程序启动到关闭,只会执行一次的代码块。
-
同步代码块 :后续多线程技术中学习。
package com.kaikeba.objectoriented.advanced;public class codeblock {public static void main(String[] args) {/*** 普通代码块:编写在顺序执行的代码流程中的代码块*/{System.out.println("==普通代码块执行==");}Person4 p1 = new Person4();Person4 p2 = new Person4();{System.out.println("==普通代码块执行==");}}
}class Person4 {private String name;private int age;/*** 构造代码块:随着对象的每次创建,执行一次,且执行在构造方法之前* 区别于构造方法的是:* 无论用户调用哪一个构造方法来创建对象,构造代码块都必然执行**/{System.out.println("构造代码块执行");}/*** 静态代码块,随着类的加载(第一次使用),静态代码块执行。* 因为类只加载一次,所以静态代码块只执行一次。*/static {System.out.println("\n静态代码块执行\n");}public Person4() {System.out.println("构造方法执行");}public Person4(String name) {System.out.println("构造方法执行");}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;}void say() {System.out.println("姓名:"+name+",年龄:"+age);}
}
结果如下:
==普通代码块执行==静态代码块执行构造代码块执行
构造方法执行
构造代码块执行
构造方法执行
==普通代码块执行==
从上面的代码可以看出一个常考的知识点:
构造方法 与 构造代码块 与 静态代码块的执行顺序:
静态代码块 —> 构造代码块 —> 构造方法
五、包
5.1 包介绍
-
把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用;
-
包如同文件夹一样,不同的包中类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,也可以避免名字冲突。
-
包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
5.2 包的使用规则
包中java文件的定义:
在.java文件的首部,必须编写类所属哪个包,格式:`package 包名;`
包的定义:
通常由多个单词组成,所有单词的字母小写,单词与单词之间使用 . 隔开,一般命名为“com.公司名.项目名.模块名...”。
规范由来:
由于Java面向对象的特性,每名Java开发人员都可以编写属于自己的Java Package,为了保障每个JavaPackage命名的唯一性,在最新的Java编程规范中,要求开发人员在自己定义的包名前加上唯一的前缀。由于互联网上的域名称是不会重复的,所以多数开发人员采用自己公司在互联网上的域名称作为自己程序包的唯一前缀。例如:com.java.xxx。
5.3 import关键字
当需要用到其他包中的类时,可以使用import导入,格式为:`import 包名.类名;`
六、权限修饰符(记住访问范围)
权限修饰符可以修饰java中所有可以访问的资源,比如类中的属性、方法、构造方法;给资源添加了一个访问权限,有如下四种权限修饰符:
七、main方法详解
main()方法一直写到了今天:public static void main(String args[])
以上的各个参数的含义如下:
-
public
:表示公共的内容,可以被所有操作所调用 -
static
:表示方法是静态的,可以由类名称直接调用。java StaticDemo09 -
void
:表示没有任何的返回值操作 -
main
:系统规定好的方法名称。如果main写错了或没有,会报错:NoSuchMethodError: main -
String[] args
:字符串数组,接收参数的
public class StaticDemo10{ public static void main(String args[]){ for(int i=0;i<args.length;i++){ System.out.println(args[i]) ; } }
};
所有的参数在执行类的时候以空格进行分割。java StaticDemo10 1 2 3 4 5 6 7
但是,如果现在我要输入的是以下几种参数“hello world”、“hello vince”、“hello mjw”。因为以空格分割,所以以上的三组参数会当做六组参数输入,那么此时如果要想完成有空格的内容输入,则参数需 要使用“"”括起来。java StaticDemo10 "hello world" "hello vince" "hello mjw"