4.1 编程语言的几个发展阶段

  • 面向机器语言
    • 机器语言
    • 汇编语言
  • 面向过程语言
    • C语言
  • 面向对象语言
    • Java
    • Python

什么是面向过程,面向对象

这里举个栗子,把大象放到冰箱里步骤

面向过程:目标是为了完成任务而完成任务,将一个个步骤细化程一个个函数,然后组织到一起

  • 打开冰箱门
  • 把大象放进去
  • 关闭冰箱门

面向对象:只要是存在的事物,可以抽象为对象,是一种抽象行为,他有 行为特征

  • fridge
    • openDoor
    • putObject
    • closeDoor
  • elephant
    • weight

那么上面的任务可描述为

firdge.openDoor()

fridge.putObject(elephant)

fridge.closeDoor()

虽然上面两种代码量差不多,但是表现方式却截然不同。

如果我们放进冰箱的不是,大象而是猴子,传统的面向过程语言可能要另写一个函数,来解决,如果我要放另一种动物,再写一个函数?由此可见面向过程的弊端 不利于组织,扩展性差

但是我们用面向对象的思想来看:大象和猴子都有共同的特性即 动物,那么我们把冰箱的行为改为放进动物,以不变应万变,这样只要是动物都能放到冰箱里,这就是面向对象特性之一 多态

4.1.1 面向对象特性

封装

所谓封装就是将数据和对数据的操作封装在一起。

比如说一个笔芯,他有 颜色,长度,等属性,他的作用是 书写

那么我们把这些东西组装起来放到一个容器中,这就是 封装(这里的容器就是对象)

多态

多态,多种运行时的状态。

我们继续拿上面例子来说,我现在有个好几种笔芯,分别是 红 黑 蓝,现在我提供了一个笔壳,他供用户使用,他可以是 红笔 蓝笔 黑笔,这里的不同颜色的笔芯都是同一类,但是他们却实现了不同的行为特征。对于用户来说,不同的笔芯都可以兼容这个笔壳,他只需要书写,无需关心笔芯实现过程。这个过程即 多态

继承

继承是一种先进的编程模式,子类可以继承父类的 特征 和 行为,但是还能有自己独有的 行为 和 特征

拿猫科动物来说,老虎和猫都是属于猫科动物,那么我们就这样定义:猫和老虎都继承自猫科动物,根据上面定义来说,毛和老虎都继承了猫科动物的 行为 和 特征,食肉,善于捕猎,但是他们又有自己单独的特点,猫会爬树,老虎擅长游泳

4.2 类

4.2.1 类声明

类是Java程序的基本要素,一个Java程序由若干个类组成,类是Java中最重要的数据类型,类声明的变量称作 对象变量 简称 对象。声明类的关键字是 class

类的定义包括两部分,类名和类体

  1. class 类名{
  2. 类体
  3. }

class 是关键字,用来定义类。

  1. class Pen{
  2. }
  3. class Pipe{
  4. }

命名规范:类名遵循 大驼峰命名习惯

  • 类名每个单词首字母大写
  • 类名最好通俗易懂,见名知意 例如BufferWriterResponseFilter``LogFactory

4.2.2 类体

类的目的是抽象出一类事物共有的特征和行为,并用特定的方式将其描述出来。

什么是类体?

类声明之后 {}包裹的内容称之为类体

抽象的关键是抓住事物的两个方面:行为 和 特征

什么是行为?

所谓行为就是,这个事物能做什么,拿笔来说他的行为就是 书写

Java中将其称之为:方法

什么是特征?

所谓特征就是,这个事物长啥样,有什么参数,拿笔来说他的特征就是 长度和宽度

Java中将其称之为:属性

既然我们知道了一个事物的行为和特征,那么我们来对它进行抽象。

  1. class 笔{
  2. 长度
  3. 宽度
  4. 行为:书写
  5. }

我们用Java语法来对其进行约束。

  1. class Pen{
  2. // 特征->属性
  3. int width;
  4. int height;
  5. // 行为->方法
  6. void write(){
  7. System.out.println("我能书写");
  8. }
  9. }

4.2.3 成员变量

通过上面的学习我们可以知道:类体的内容可分为两部分 变量的声明 和 方法的定义,声明变量部分声明的变量称之为 成员变量 或 域变量

成员变量的类型

成员变量类型可以是Java中任何一种数据类型,包括基本数据类型:整型,浮点型, 字符型,逻辑型引用类型:数组 对象 接口

  1. class Animal{
  2. // 引用类型
  3. String[] alias;
  4. // 基本类型
  5. int age;
  6. }

成员变量的有效范围

成员变量在 整个类内都有效,与他在类体中的先后 位置无关

我们更习惯将,成员变量集中放在类体头部。因为按照正常思维逻辑来说 先介绍属性后描述行为,更为贴切。

编程风格

成员变量命名规范符合 小驼峰命名法

  • 一行只声明一个变量。
  • 单词首字母小写,后面每个单词首字母大写。例如userName `
  • 变量名字应当见名知意,避免使用a b c d这样的变量名字,会给后期代码维护带来困难

4.2.4 方法

方法的定义包括两部分:方法头方法体

  1. 方法头{
  2. 方法体
  3. }

方法头

方法头由方法 返回值方法 名称和方法 参数列表构成

返回值指定该方法被执行后要 返回什么样的内容

方法名表示方法的名字

参数列表由一系列变量的声明组成,他们由逗号分隔开,可以是任意的Java数据类型

  1. int add(int a,int b){
  2. return a+b;
  3. }
  4. void speak(String name){
  5. System.out.println("Hello",name);
  6. }

如果方法返回值是 void时该方法就不需要返回数据

方法的命名和成员变量一致,均采用 小驼峰命名法

方法体

方法声明后,由 {}包裹的内容称为方法体,方法体中声明的变量称之为 局部变量

局部变量只在方法内有效,而且与其声明位置有关。不能在声明前使用局部变量

方法的参数在整个方法内有效。

局部变量的有效范围

如果局部变量声明在一个复合语句中,那么该局部变量的有效范围就是该复合语句,如果局部变量声明在一个循环语句中,那么该局部变量有效范围就该循环语句。

区分成员变量和局部变量

如果成员变量和局部变量同名,那么成员变量将会被隐藏掉 就近原则

通俗点来说,就是如果一个方法中的局部变量和成员变量的名字相同,那么在使用该变量时,总是使用局部变量,成员变量再当前方法作用域内会被自动隐藏。

  1. class Cat{
  2. String name = "CatA";
  3. public void test(){
  4. String name = "CatB";
  5. System.out.println(name); // CatB
  6. }
  7. }

这时候就有人要问了,那我就是要用成员变量的name,你能把我怎么办

如果在成员变量被屏蔽的情况下访问成员变量需要使用 this关键字

  1. class Cat{
  2. String name = "CatA";
  3. public void test(){
  4. String name = "CatB";
  5. System.out.println(this.name); // CatA
  6. }
  7. }

这里使用 this.name 就制指定了我要访问的是类成员变量name,而不是局部变量name。

默认值

  • 类成员变量有默认值
  • 局部变量没有默认值
  1. class Cat{
  2. String name;
  3. public void test(){
  4. String name;
  5. System.out.println(name); // 局部变量
  6. System.out.println(this.name); // 类成员变量
  7. }
  8. }

编译报错

  1. java: 可能尚未初始化变量name

我们在使用局部变量前,必须要为其 指定默认值

小细节

我们在声明类成员变量同时可赋值。

但是不可以在声明后,直接为其赋值,只能在方法体内赋值。

也就是说单独的赋值语句不能出现在类成员变量声明区域

  1. class A{
  2. int a; //声明,未赋值 ok
  3. int b =10; //声明,并赋值 ok
  4. int c;
  5. c=20; //error 不能这样赋值,如果在类成员变量初始化后赋值,只能在方法体内.
  6. }

4.2.5 类的UML图

此处省略….自行发挥

4.3 构造方法与对象的创建

类是对象的模板,没有类就没有对象。

4.3.1 构造方法

构造方法是类中一种特殊方法,当程序用类创建对象时调用的方法叫做构造方法。

构造方法的特点

  • 构造方法的名字和类名完全相同
  • 没有返回值
  • 构造方法允许重载
  • 如果一个类中没有编写构造方法,系统会默认生成一个没有参数的构造方法

默认构造方法与自定义构造方法

如果一个类中定义了一个或多个构造方法,那么Java将不再提供默认的构造方法。

  1. class Point {
  2. int x;
  3. int y;
  4. // 无参构造方法
  5. Point(){
  6. }
  7. // 带参数的构造方法
  8. Point(int x, int y) {
  9. this.x = x;
  10. this.y = y;
  11. }
  12. // 构造方法重载:参数列表不同,方法名相同
  13. Point(Point p){
  14. this.x=p.x;
  15. this.y=p.y;
  16. }
  17. }

一定要记住:构造方法没有返回值,一旦有了返回值就不再是构造方法。

4.3.2 创建对象

创建一个对象包括 声明对象和为对象 分配变量两个步骤

对象的声明

  1. 类名 对象名;

例如

  1. Point start

为声明的对象分配变量

为一个已经声明的对象分配变量需要使用关键字 new,后面跟上类的构造方法,即可。

如果类中没有构造方法,那么JDK会自动生成一个 默认无参数构造方法

  1. // new 构造方法();
  2. start = new Point();

当然声明对象和分配变量可合并成一行代码

  1. Point end = new Point(10,20);

只声明的对象值为null,是不能直接拿来用的,如果使用,会引发 空指针异常,所以我们要用构造方法来对其进行初始化。

4.3.3 使用对象

抽象的目的是产生类,而类的目的是创建具有属性和行为的对象,对象不仅可以操作自身变量来改变状态,还能调用类中方法产生一定的行为。

对象操作属性

体现对象的特征

在确保属性可被访问的情况下,我们这样通过对象访问属性.

  1. 对象名.属性名

对象操作类中的方法

体现对象的行为

对象创建后,可使用 .来访问类中的方法

  1. 对象名.方法名()

体现封装

当我们给通过类对象调用方法,使用类成员变量时,他将 数据和对 数据的操作绑定到了一起,这就是一定程度上的封装,外部使用者只需要通过对象去调用,而无需关心类内部实现细节。

4.3.4 对象的引用和实体

类是一种数据类型,类声明的变量叫做 类实例化对象,对象存放着真实对象的引用.

避免使用空对象

没有实体的对象称作为空对象,空对象不能使用,如果使用了一个空对象会引发 空指针异常,所以在我们写bug时要避免使用空对象。

还有一件事

一个类声明的两个对象具有相同的引用,二者共用一个对象,但是如果两个对象都是实例化而来,那么这两个对象是相互隔离的。

  1. public class Test {
  2. public static void main(String[] args) {
  3. Point p = new Point(10, 20);
  4. Point temp = p;
  5. // 二者共用同一个对象
  6. System.out.println(p);
  7. System.out.println(temp);
  8. System.out.println("-------");
  9. Point p1 = new Point(10, 20);
  10. Point p2 = new Point(10, 20);
  11. // 这里 p1 和p2是相互隔离的
  12. System.out.println(p1);
  13. System.out.println(p2);
  14. }
  15. }
  1. Point@60e53b93
  2. Point@60e53b93
  3. -------
  4. Point@5e2de80c
  5. Point@1d44bcfa

垃圾收集

当一个对象的引用不再被任意对象持有,那么这个对象会被系统回收,这是Java独有的垃圾回收机制(GC),正是有这种独有的垃圾回收机制,所以Java没有 析构方法,所以Java很少出现内存泄漏这样的致命问题。

扩展延伸:手动触发GC

  1. System.gc();
  2. // 当前命令会通知JVM进行垃圾回收,误区:并不是立马就进行垃圾回收

4.4 类与程序的基本结构

一个Java应用程序由若干个类构成,Java程序有一个主类,即含有 mian方法的类,Java应用程序从main方法开始执行。

通常情况下我们建议,一个类用一个单独的文件来存储。

如果一个文件中有多个类,那么有且只有一个类的访问权限修饰符为public,否则会引发编译错误.

4.5 参数传值

在方法调用时会发生参数传递,参数变量必须有确切的值

4.5.1 传值机制

  • 基本类型深拷贝 ,即开辟一个新变量来克隆当前传递过来的参数
  • 引用类型浅拷贝,共用同一块内存空间
  1. class Number {
  2. int val;
  3. }
  4. public class Test {
  5. // 基本数据类型传递
  6. public static void modify(int x) {
  7. x = 20;
  8. }
  9. // 引用数据类型传递
  10. public static void modify(Number number) {
  11. number.val = 20;
  12. }
  13. public static void main(String[] args) {
  14. int x = 10;
  15. modify(x);
  16. System.out.println(x);
  17. System.out.println("---------");
  18. Number number = new Number();
  19. number.val = 10;
  20. modify(number);
  21. System.out.println(number.val);
  22. }
  23. }
  1. 10
  2. ---------
  3. 20

4.5.2 可变参数

顾名思义参数的数量可变,在声明方法时使用 ...表示可变参数

我们可以使用for循环来遍历可变参数

  1. class Math{
  2. int sum(int ...var){
  3. int s = 0;
  4. for (int t : var) {
  5. s+=t;
  6. }
  7. return s;
  8. }
  9. }
  10. public class Test {
  11. public static void main(String[] args) {
  12. Math math = new Math();
  13. int sum = math.sum(10, 20, 30, 40, 50);
  14. System.out.println(sum);
  15. }
  16. }
  1. 150

如果可变参数和别的参数同时出现,那么可变参数一定在最后面,不然编译器无法判断

  1. void sum(int n,...int x) // ok
  2. void sum(int... x,int n) // error

4.6 对象的组合

组合就是将一个现有对象拿过来成为自己的一部分,打不过就加入 hhh~

4.6.1 组合与复用

如果对象a组合了对象b,那么a就可以通过委托b来调用b的方法,我们把这个过程称之为 复用

  1. // 笔芯
  2. class Refill {
  3. String color;
  4. void write() {
  5. System.out.println("正在书写,我的颜色是:" + color);
  6. }
  7. }
  8. // 笔组合了笔芯
  9. class Pen {
  10. Refill refill;
  11. public void setRefill(Refill refill) {
  12. this.refill = refill;
  13. }
  14. // 复用 间接调用笔芯书写
  15. void write() {
  16. refill.write();
  17. }
  18. }
  19. public class Test {
  20. public static void main(String[] args) {
  21. // 初始化笔芯
  22. Refill refill = new Refill();
  23. refill.color = "黑色";
  24. Pen pen = new Pen();
  25. // 设置笔芯
  26. pen.setRefill(refill);
  27. pen.write();
  28. }
  29. }
  1. 正在书写,我的颜色是:黑色

为什么这里要写:setRefill方法?

首先这是Java的编码规范用专业术语来说叫做 setter,其次我们知道 成员变量类型如果是类类型,那么它的初始值为null,我们直接使用的话会引发空指针异常,所以我们要在调用前对其进行初始化。

4.6.2 关联关系和依赖关系

关联关系

如果A类程成员变量类型是B类声明的对象,那么我们把这种关系叫做 关联,UML图中用 实线表示

上面的笔芯的例子就是关联关系

依赖关系

如果A类某个方法参数,是用B类声明的对象,那么我们把这种关系叫做 依赖,UML图中用 虚线表示

设置笔芯的方法就是用的关联关系,一定程度上可以减少程序耦合性.

4.7 实例成员与类成员

4.7.1 实例变量和类变量的声明

类成员变量可分为 实例变量类变量,声明变量时使用 static关键字修饰称作类变量,否则称为实例变量

  1. class Cat{
  2. // 类变量
  3. static int count;
  4. // 成员变量
  5. String name;
  6. }

需要注意的是 static 要放在变量类型前面

4.7.2 实例变量和类变量的区别

  • 不同对象的实例变量是相互隔离的
  • 所有对象共享类变量
  • 可以通过类名直接访问类变量

不同的实例对象的实例变量互不相同,多个实例对象的类变量共享.

思考:如果我要实现一个实例对象计数器该如何实现?

比如说

  1. Dog d1 = new Dog();
  2. Dog d2 = new Dog();
  3. // 此时计数器的值为 2

这时候就有人说了定义一个局部变量,每次实例化时局部变量自增。好像确实也没啥问题,但是未免耦合度也太高了吧,不是说类是为了降低耦合性吗?

还有没有更好的解决方案?

4.8 方法重载

Java中存在两种多态:重载(Overload)重写(Overrid).
方法重载是两种多态中的一种,例如你让一个人求面积,他可能会问你求什么面积? 所谓多态性:指的是向功能传递不同的消息,以便让对象根据对应的消息来产生相应的行为。 对象的行为通过类中方法来体现,那么行为的多态性就是方法的重载。

4.8.1 方法重载的语法规则

  • 参数个数不相同
  • 参数列表不同
  1. public class People {
  2. public void sayHello(){
  3. System.out.println("Hello,World!");
  4. }
  5. public void sayHello(String name){
  6. System.out.println("Hello,"+name);
  7. }
  8. public static void main(String[] args) {
  9. People people = new People();
  10. people.sayHello();
  11. people.sayHello("jack");
  12. }
  13. }

上面代码中,sayHello方法就是重载方法。

因此我们可以得出重载方法的定义: 方法名相同但是方法参数列表不同的方法,我们可以将其称之为重载方法.

4.8.2 避免重载出现歧义

重载方法之间必须保证互相的参数列表不相同,但是需要小心的是,重载方法在被调用时会出现歧义调用,也就是我们常说的二义性。

例如

  1. public class Calculate {
  2. public int sum(int a,int b){
  3. return a+b;
  4. }
  5. public int sum(double a,double b){
  6. return a+b;
  7. }
  8. public static void main(String[] args) {
  9. System.out.println(new Calculate().sum(10, 20));
  10. }
  11. }

乍一看这代码似乎没有什么问题,但是我们仔细研究不难发现这里面出现了二义性问题,低转高自动转 这个我们前面学过,所以在低10行,我们传入10,20 时出现了歧义,编译器不知道该调用哪个方法,所以抛出错误

  1. java: 不兼容的类型: double转换到int可能会有损失

因此我们在平时开发时要注意这个问题。

4.9 this关键字

this是一个关键字,表示某个对象,this可出现在实例方法和构造方法中,但是不能出现在类方法中(好好思考下,为什么?)

4.9.1 在构造方法中使用this

this关键字出现在构造方法中,代表使用该构造方法所创建的对象,通俗来讲this 就是当前对象

  1. public class People {
  2. private String name;
  3. public People(String s){
  4. name=s;
  5. this.debug(name);
  6. }
  7. public void debug(String v){
  8. System.out.println(v);
  9. }
  10. public static void main(String[] args) {
  11. new People("jack");
  12. }
  13. }

上面代码我们在第5行时通过this调用了debug方法,但是我们不难发现即使去掉this,我们也能直接调用debug方法,那么this这个关键字是不是就画蛇添足了呢?

那么我们接下来看这个代码

  1. public class People {
  2. private String name;
  3. People(String name){
  4. name =name;
  5. }
  6. public String getName() {
  7. return name;
  8. }
  9. public static void main(String[] args) {
  10. System.out.println(new People("jack").getName());
  11. }
  12. }

运行后结果是多少? 认真思考哦

为什么会出现这样的问题?如何解决?

4.9.2 在实例方法中使用this

实例方法只能通过对象来调用,不能通过类名来调用。当this关键字出现在实例方法中时,this就代表正在调用该方法的当前对象。 谁调用this,this就代表谁

当变量名称出现二义性时可用this来制定使用哪个变量

  1. class A{
  2. int memVar;
  3. static int staticVar;
  4. void test(){
  5. System.out.println(memVar);
  6. System.out.println(staticVar);
  7. }
  8. void test(int t){
  9. int memVar =10;
  10. int staticVar = 20;
  11. System.out.println(this.memVar);
  12. System.out.println(A.staticVar);
  13. }
  14. }

通过前面的学习我们知道,访问类变量我们要用类名.变量名 我们可以省略掉 类名。

但是上面这个代码就是一个特殊的情况,我们看 第 8行 test 方法,里面 局部变量和成员变量重名,从而导致了二义性问题:编译器无法找到具体使用的是哪个变量。所以在这种情况下我们就需要使用this 来制定使用的是成员变量,同样我们使用A来指定使用的是 A.staticVar 这个类成员变量。

4.10 包

什么是包?

包是Java语言有效管理类的一个机制。 包是用来组织类的

4.10.1 包语句

通过关键字 package 声明包。

package语句作为Java源文件的第一条语句,指明该源文件定义的类所在的包,即为该源文件中声明的类指定包名。

  1. package 包名;

如果源程序中省略了package语句,源文件中所定义命名的类,被隐含的认为是一个无名包的一部分,只要这些类的字节码放在相同的目录中,那么他们就属于同一个包,但没有包名。

包名可以是一个合法的标识符,也可以是若干个标识符+.分隔而成

  1. package com.lu.j2se.base;

4.10.2 有包名的类的存储目录

如果一个类有包名,那么久不能在任意位置存放它,否则虚拟机将无法加载这样的类。

通常来说我们更习惯将包名与本地文件夹名称关联起来。例如

  1. - src
  2. - com
  3. - lu
  4. - j2se
  5. - base
  6. package com.lu.j2se.base;

4.10.3 运行有包名的主类

需要使用 全限定类名

什么是全限定类名?

完整的吧包名+类名

就和地名一样: 中国.山西.临汾

  1. javac *.java
  2. java com.lu.j2se.base.Test

Java不允许用户程序使用 java作为包名的一部分,例如java.xxxxx 是非法的包名(运行会发生异常)

4.11 import语句

一个类可能需要另一个类声明的对象作为自己成员或者方法中的局部变量,如果这两个类在同一个包中,当然没什么问题。但是如果一个类要引用别的包中的类,那么就需要使用import 语句来导入。

4.11.1 引入类库中的类

用户编写的类和类库中的类不在一个包中。如果用户需要使用类库中的类,就必须使用 import 语句。 import 语句可以引入包中的类和接口。 Java大约提供了130多个包

  • java.lang 包含所有基本语言类
  • javax.swing 图形包
  • java.io 输入输出流
  • java.util 包含工具类
  • java.sql 数据库操作类
  • java.net 包含所有实现网络功能的类

如果要引入一个包中的全部类,则可以使用通配符*来代替

例如

  1. import java.util.*;

也可以按需引入

  1. import java.util.Data;

获取当前时间戳

  1. import java.util.Date;
  2. public class CurrentDate {
  3. public long now(){
  4. Date date = new Date();
  5. return date.getTime();
  6. }
  7. public static void main(String[] args) {
  8. long now = new CurrentDate().now();
  9. System.out.println(now);
  10. }
  11. }

4.11.2 引入自定义包中的类

同样我们自己定义的包也可以使用 import 关键字来引入

  1. import com.lu.pojo.controller.*;

4.12 访问权限

通过之前的学习我们已经知道了: 当用一个类创建一个对象后,该对象可以通过”.”运算符来操作自己的成员变量,或者使用类中的方法。

提到访问权限不得不提到面向对象的特性之一,封装

4.12.1 何谓访问权限

访问权限顾名思义就是,访问者有哪些权限? hhh 好一个废话文学。

不过话说回来,确实如此。

访问权限是用来对访问者的权限进行限制。通俗点来说就是访问者能干什么,不能干什么。

在java中用访问权限修饰符来对访问者的权限进行限制。

  • public 公开的
  • protected 受保护的
  • private 私有的

具体访问限制查看下表

修饰词 本类 同一个包的类 继承类 其他类
private × × ×
无(默认|友好) × ×
protected ×
public

4.12.2 私有变量和私有方法

通过关键字 private 可以修饰成员变量,或者成员方法为私有变量,或者私有方法。但是自己可以访问,比如说女孩子的年龄,别人不知道,他自己总该知道叭。

  1. class Boy{
  2. int age;
  3. String name;
  4. public Boy(int age, String name) {
  5. this.age = age;
  6. this.name = name;
  7. }
  8. }
  9. class Girl {
  10. private int age;
  11. String name;
  12. public Girl(int age, String name) {
  13. this.age = age;
  14. this.name = name;
  15. }
  16. }
  17. public class Main{
  18. public static void main(String[] args) {
  19. Girl girl = new Girl(21,"xiao_hong");
  20. Boy boy = new Boy(22,"ff");
  21. // ==================
  22. System.out.println("gril:"+girl.name);
  23. // System.out.println(girl.age); // 美少女的事情你少打听
  24. System.out.println("boy:"+boy.name);
  25. System.out.println("boyAge:"+boy.age);
  26. }
  27. }

通过第29行代码,我们发现这里的 age 无法被访问, 仅仅是因为 美少女的事情你少打听吗?

显然不是这样的,听我狡辩,不对听我解释

第14行代码,我们给Girl类的age关键字前面加了个 private ,表示这个属性是不可被外部访问者访问的。

方法也是如此,这里就详细概述了…. (你别偷懒动手敲一下)

  1. class Boy{
  2. int age;
  3. String name;
  4. public Boy(int age, String name) {
  5. this.age = age;
  6. this.name = name;
  7. }
  8. public void beFriend(){
  9. // 交往
  10. System.out.println("我叫"+name+",我"+age+"岁了,我们交往吧!");
  11. }
  12. public void checkPhone(){
  13. System.out.println("宝,这是我的手机,检查吧!");
  14. }
  15. public void breakup(){
  16. System.out.println("呸!渣女,手机都不让看,分手吧!");
  17. }
  18. }
  19. class Girl {
  20. private int age;
  21. String name;
  22. public Girl(int age, String name) {
  23. this.age = age;
  24. this.name = name;
  25. }
  26. public void beFriend(){
  27. // 交往
  28. // 只有自己知道自己多少岁了
  29. System.out.println("我叫"+name+",我"+age+"岁了,我们交往吧!");
  30. }
  31. private void checkPhone(){
  32. System.out.println("不能看我手机");
  33. }
  34. }
  35. public class Main{
  36. public static void main(String[] args) {
  37. Girl girl = new Girl(21,"xiao_hong");
  38. Boy boy = new Boy(22,"ff");
  39. // 男女交往
  40. boy.beFriend();
  41. girl.beFriend();
  42. // 检查手机
  43. boy.checkPhone();
  44. // girl.checkPhone()
  45. // 女孩不给看手机,分手!
  46. boy.breakup();
  47. }
  48. }

4.12.3 共有变量和共有方法

public 关键字修饰的成员变量或者成员方法,称之为共有方法/变量

如果一个类使用 public 关键字来修饰成员变量或者方法, 那么被修饰的成员变量/方法,是可以在外部直接被访问的。 没有什么要强调的 略….

4.12.4 友好变量和友好方法

不使用 public private protected 访问权限修饰符修饰的变量/方法,称之为 友好变量/方法。

关于友好访问权限修饰符,在同一个包内都可以被访问到

4.12.5 受保护的成员变量和方法

这个比较特殊,当前包,和子类都能访问。

什么是子类?

这里就提到了面向对象第三个特性,继承

  • 被继承的类称之为父类/超类/基类
  • 继承的类称之为子类/派生类

4.12.6 public 类与友好类

如果在 class 关键字前面 加个 public 关键字称之为这样的类是一个 public 类,

  1. public class A{}

通常我们更习惯将类拆分到多个文件中

那么就有个这样的约束 一个 java 文件中 只能有一个 public 类

借助这样的唯一特性, public 类 决定了 java文件的文件名(要小写奥)

例如

  1. public class Main{
  2. }
  3. class Test{
  4. }

那么这个java文件名就是 main.java

那么来举一反三下叭

  1. class A{
  2. }
  3. public class Animal{
  4. }
  5. class B{
  6. }
  1. class Dog{}
  2. public class Cat{}
  • 不能使用 privateprotected 来修饰类
  • 访问权限修饰符权限高低排列: public protected 友好的 private

4.13 基本类型 的封装

来复习下 基本类型都有哪些?

Java提供了与基本类型相关的类,实现了对基本数据类型的封装。这些类在 java.lang包中

分别是

Byte Integer Short Long Float Double 和 Character

4.13.1 Double和 Float类

Double类和Float类实现了对,double 和float 基本数据类型的类包装。

  • 可以使用Double(double num)的构造方法创建一个Double类型的对象 doubleValue() 取值
  • 可以使用Float(float num) 的构造方法创建一个Float类型的对象 floatValue() 取值

4.13.2 Byte Short Integer Long类

如法炮制, Byte Short Integer Long 分别是 byte short integer long 的包装类
那么根据上面的 “规则” 我们也可以猜到

xxxValue 就是用来取值
果不其然 byteValue() shortValue() intValue() longValue() 就可以实现取值

4.13.3 Character类

可以使用 Character(char c) 构造方法来创建一个 Character对象

同样的 通过 charValue 实现取值

demo

  1. public class VarBox {
  2. public static void main(String[] args) {
  3. Character f = new Character('f');
  4. Short aShort = new Short((short)10 );
  5. Boolean aBoolean = new Boolean(false);
  6. Byte aByte = new Byte((byte) 1);
  7. Integer integer = new Integer(20);
  8. Long aLong = new Long(100L);
  9. Float aFloat = new Float(3.14F);
  10. Double aDouble = new Double(3.14);
  11. System.out.println(f.charValue());
  12. System.out.println(aShort.shortValue());
  13. System.out.println(aBoolean.booleanValue());
  14. System.out.println(aByte.byteValue());
  15. System.out.println(integer.intValue());
  16. System.out.println(aLong.longValue());
  17. System.out.println(aFloat.floatValue());
  18. System.out.println(aDouble.doubleValue());
  19. }
  20. }

这时候你肯定就要问了? 已经有基础数据类型了为啥还要多此一举弄他们的包装类?
不要急,接着往下看

4.13.4 对象数组

现在给你一个任务将一段字符串中的小写转换为大写,大写转换为小写,其他的用 _ 替代,如何写?

  1. public class VarBox {
  2. public static void main(String[] args) {
  3. String str = "Hello,World!";
  4. char[] chars = str.toCharArray();
  5. for (int i = 0; i < chars.length; i++) {
  6. if (Character.isLowerCase(chars[i])) {
  7. chars[i] = Character.toUpperCase(chars[i]);
  8. } else if (Character.isUpperCase(chars[i])) {
  9. chars[i] = Character.toLowerCase(chars[i]);
  10. } else {
  11. chars[i] = '_';
  12. }
  13. }
  14. System.out.println(chars);
  15. }
  16. }

不难发现,借助面向对象封装的特性,我们的包装类为我们提供了很多实用的方法。这也是和C语言最大的不同,我们无需自己判断只需要调用包装类已经写好的方法即可。 现在还会觉得包装类是多此一举吗。

4.15 JRE扩展与jar文件

Java的运行环境提供的类库只是核心类,不可能满足用户的全部需求,为此Java提供了扩展,只需要将对应的字节码文件打包后,其他用户就能使用。Java经过数十年的生态积累有了非常多的jar包供我们选择。

https://mvnrepository.com/

这是java仓库,有几十万个包供我们选择,基础阶段暂时用不不到,可以先了解 maven 仓库.

hhh 又扯远了,话说回来我们自己该如何制作jar包呢?

可以通过 jar.exe 命令把一些类的字节码文件压缩成一个jar文件,然后将这个jar文件放到java运行环境jre 扩展中即可。

这里了解即可,不是重点 IDEA已经有了可视化打包方案,所以这里就不费口舌啦。

4.16 文档生成器

这里是上面内容的扩展延伸,我们生成了jar包别人该如何使用,必须要有一个确切的使用说明书,所以文档就诞生了。

什么是文档?
文档就是用来描述如何使用你写的工具包。


那就动手试试吧

我们实现一个计算器的类,加减乘除。

回顾下我们前面学的如何抽象成一个类,抓住一个物体的行为和特征

那么我们这里

  • 行为只有四种 加(add) 减(sub) 乘(mul) 除(div)
    特征看似没有? 但是真的没有嘛?

我们计算器有两个操作数, 这就是特征。

  • 特征有两个 num1(操作数) num2(被操作数)

既然有了行为和特征 那么我们就可以抽象出类

4. 类和对象 - 图1 这里考虑到初学者对于代码的恐惧代码就能省则省。

代码实现

  1. public class Calculate {
  2. double num1;
  3. double num2;
  4. public Calculate(double num1, double bum2) {
  5. this.num1 = num1;
  6. this.num2 = bum2;
  7. }
  8. public double add(){
  9. return num1+num2;
  10. }
  11. public double sub(){
  12. return num1-num2;
  13. }
  14. public double mul(){
  15. return num1*num2;
  16. }
  17. public double div(){
  18. return num1/num2;
  19. }
  20. }
  21. class TestCalc{
  22. public static void main(String[] args) {
  23. Calculate calc = new Calculate(22, 7);
  24. System.out.println(calc.add());
  25. System.out.println(calc.sub());
  26. System.out.println(calc.mul());
  27. System.out.println(calc.div());
  28. }
  29. }

运行结果

  1. 29.0
  2. 15.0
  3. 154.0
  4. 3.142857142857143

既然上面学了文档生成 那么给我们的 计算器工具生成一个文档叭

首先给我们的代码加亿点点注释

  1. package base;
  2. /**
  3. * @author luckyfang
  4. * @version 1.0
  5. */
  6. public class Calculate {
  7. double num1;
  8. double num2;
  9. /**
  10. *
  11. * @param num1 第一个操作数
  12. * @param bum2 第二个操作数
  13. */
  14. public Calculate(double num1, double bum2) {
  15. this.num1 = num1;
  16. this.num2 = bum2;
  17. }
  18. /**
  19. *
  20. * @return 两个操作数之和
  21. */
  22. public double add(){
  23. return num1+num2;
  24. }
  25. /**
  26. *
  27. * @return 两个操作数之差
  28. */
  29. public double sub(){
  30. return num1-num2;
  31. }
  32. /**
  33. *
  34. * @return 两个操作数之积
  35. */
  36. public double mul(){
  37. return num1*num2;
  38. }
  39. /**
  40. *
  41. * @return 两个操作数之商
  42. */
  43. public double div(){
  44. return num1/num2;
  45. }
  46. }
  47. class TestCalc{
  48. public static void main(String[] args) {
  49. Calculate calc = new Calculate(22, 7);
  50. System.out.println(calc.add());
  51. System.out.println(calc.sub());
  52. System.out.println(calc.mul());
  53. System.out.println(calc.div());
  54. }
  55. }

然后使用 javadoc 命令生成文档

  1. javadoc -d apidoc Calculate.java
  2. 正在加载源文件Calculate.java...
  3. 正在构造 Javadoc 信息...
  4. 标准 Doclet 版本 1.8.0_291
  5. 正在构建所有程序包和类的树...
  6. 正在生成apidoc/base/Calculate.html...
  7. 正在生成apidoc/base/package-frame.html...
  8. 正在生成apidoc/base/package-summary.html...
  9. 正在生成apidoc/base/package-tree.html...
  10. 正在生成apidoc/constant-values.html...
  11. 正在构建所有程序包和类的索引...
  12. 正在生成apidoc/overview-tree.html...
  13. 正在生成apidoc/index-all.html...
  14. 正在生成apidoc/deprecated-list.html...
  15. 正在构建所有类的索引...
  16. 正在生成apidoc/allclasses-frame.html...
  17. 正在生成apidoc/allclasses-noframe.html...
  18. 正在生成apidoc/index.html...
  19. 正在生成apidoc/help-doc.html...

最后就能看到啦

4.17 对象的组合与复用

组合

组合: 一个类中的对象可以将其他类的对象作为自己的组成部分,这个过程就叫做对象的组合。

就是我们前面说的关联关系

  1. class Pipe{
  2. public void write(){
  3. System.out.println("正在书写");
  4. }
  5. }
  6. public class Pencil {
  7. public Pencil(Pipe pipe) {
  8. this.pipe = pipe;
  9. }
  10. private Pipe pipe;
  11. public void w(){
  12. pipe.write();
  13. }
  14. }
  15. class PencilMain{
  16. public static void main(String[] args) {
  17. new Pencil(new Pipe()).w();
  18. }
  19. }

上面代码中将笔芯组合为自己的一部分,就是组合。

复用

完成对象组合后,通过组合对象调用其中的方法

就是我们前面说的依赖关系

  1. class Pipe{
  2. public void write(){
  3. System.out.println("正在书写");
  4. }
  5. }
  6. public class Pencil {
  7. public void w(Pipe pipe){
  8. pipe.write();
  9. }
  10. }
  11. class PencilMain{
  12. public static void main(String[] args) {
  13. new Pencil().w(new Pipe());
  14. }
  15. }

这里 w 方法 调用了 pipe 的write,这就是复用。


和uml图的关系

  • 组合在uml图中使用实线
  • 复用在uml图中使用虚线

4.18 小结

  • 类是组成Java源文件的基本元素,一个源文件是由若干个类组成的。
  • 类体可以有两种重要的成员: 成员变量和方法
  • 成员变量可以分为实例变量和类变量。
    • 类变量被该类所有对象共享
    • 不同对象的实例变量互不相同(互相对立)
  • 除构造方法外,其他方法分为实例方法类方法
    • 类方法不仅可以由该类的对象调用,也可以用类
    • 实例方法必须由对象来调用
  • 实例方法既可以操作实例变量,也可以操作类变量
    • 当对象调用实例方法时,方法中的成员变量就是指分配给该对象的成员变量
    • 实例变量和其他对象不同,占用不同的内存空间
    • 类变量和其他对象相同,占有相同的内存空间
    • 类方法只能操作类变量,当对象调用方法时方法中成员变量一定都是类变量,类变量是共享的
  • 通过对象的组合可以实现复用
  • 编写Java源文件时,可以使用 import 语句导入包
  • 对象访问自己变量以及调用方法受权限访问控制