# 大数据学习笔记第7天 - 面向对象1 #
## 回顾
### 二维数组
元素本身是一维数组的数组。
三种初始化方式:
1) int[][] arr = new int[2][];
2) int[][] arr = new int[2][2];
3) int[][] arr = new int[][]{{1,2}, {3,4,5}}; // 简化写法见第4种
4) 简化写法省略掉公共的部分:int[][] arr = {{1,2}, {3,4,5}};
### 面向对象OOP
类的设计:一定是先有具体事物或者脑海中想象中的事物.然后对属性和行为进行抽取.
现实中的事物属性 --> 成员变量
现实中的事物行为(功能) --> 成员方法
类一般情况下不能直接使用.主要用来创建对象.
new 类名(); <-实际上就是在调用空参构造方法
面向对象的三大特点:封装,继承,多态.
匿名对象:
没有名对象(没有变量引用到的对象)
使用场景:
1.某个对象的方法只使用一次
2.对象作为参数传递给方法
3.对象作为返回值返回
自定义类的使用:
1.可以作为形参类型
public void test(Student s){...}
实际上需要的是此类的一个对象
2.可以作为返回值类型
public Student getInstance(){
...
//Student s = new Student();
//return s;
return new Student();
}
封装:把不想让外界操作的成员隐藏起来.
实现:使用private关键字
对成员变量使用private修饰,提供公有的get/set方法.
this关键字:
1.区分成员变量和局部变量
2.本类中方法之间互相调用(此时this可以省略)
3.构造方法之间互相调用(this必须是第一条语句)
构造方法:
他也是一种成员方法.
特点:与类同名没有返回值(没有返回值类型定义,没有return关键字)
多个构造方法以重载的形式出现.
如果没有提供任何构造方法,系统默认有一个空参构造方法,方法体为空.
如果提供构造方法,系统就不再提供任何构造方法.
标准的javabean:
1.私有的成员变量
2.公有的get/set方法
3.空参构造方法
### 作业
自定义Laptop类,做好封装,并测试。
*E:\01\01 Java\day07\code*
/*
自定义类
*/
class Laptop{
//成员属性
private String brand;
private int price;
//封装方法
public void setBrand(String brand){
this.brand = brand;
}
public String getBrand(){
return brand;
}
public void setPrice(int price){
this.price = price;
}
public int getPrice(){
return price;
}
//构造函数
public Laptop(){}
public Laptop(String brand){
this.brand = brand;
}
public Laptop(int price){
this.price = price;
}
public Laptop(String brand, int price){
this(brand);
this.price = price;
}
//成员方法
public void show(){
System.out.println("Brand:" + brand + ", Price:" + price);
}
}
*E:\01\01 Java\day07\code*
/*
测试类
*/
public class LaptopDemo{
public static void main(String[] args){
Laptop pc = new Laptop("Dell", 3500);
pc.show();
}
}
效果如图:
![](./img/07/2018-08-18_142004.png)
## 大纲
### 第一节课
- 面向对象编程思想
- 类与对象的关系
- 封装private
- 构造方法
- this关键字
- static关键字(javadoc制作工具类)
- 代码块
### 第二节课
- static关键字
- main方法传参
- 工具类的制作
- API文档的使用
- 代码块
### 第三节课
- 继承
- 子类使用变量的原则
- super关键字
- 方法重写与子类对象实例化
- final关键字
### 第四节课
- 抽象类
- 抽象方法
- 接口
<hr>
## 面向对象编程思想
### 1. 理解面向对象
- 面向对象是相对面向过程而言
- 面向对象和面向过程都是一种思想
- 面向对象是基于面向过程的
![](./img/07/oop-01.jpg)
![](./img/07/oop-02.jpg)
### 举例
- 电脑组装
- 建造汽车
- 泡咖啡
- 使用JDK的Scanner类
### 2. 面向对象的特点
- 是一种符合人们思考习惯的思想
- 可以将复杂的事情简单化
- 将程序员从执行者转换成指挥者
- 完成需求时:
- 先要去找具有所需的功能的对象来用
- 如果该对象不存在,那么创建一个具有所需功能的对象
- 简化开发并提高复用
### 3. 面向对象开发、设计、特征
- 开发的过程:其实就是不断的创建对象,使用对象,指挥对象做事情;
- 设计的过程:其实就是在管理和维护对象之间的关系;
- 面向对象的特征:
- 封装
- 继承
- 多态
### 3.2 类与对象的关系
- 使用计算机语言就是不断的在描述现实生活中的事物
- java中描述事物通过类的形式体现,类是具体事物的抽象,概念上的定义
- 对象(实例)即是该类事物实实在在的个体
- 万物皆对象
![](./img/07/oop-03.jpg)
### 什么是类?
可以理解为:
- 类就是从对象中抽象出来的共性;
- 具体学生对象就是实实在在的个体;
- 一个类可以创建多个对象;
### 实例:Dog类
![](./img/07/oop-04.jpg)
定义一个类主要是两方面内容:成员变量(事物的属性)、成员方法(事物的行为)。
/*
定义一个Dog类:
成员属性:事物的属性
成员方法:事物的行为
*/
public class Dog{
// 成员属性:名字、年纪、性别
String name="wancai";
int age=2;
int sex=0;
// 成员方法:吃、喝、叫
public void eat(String food){
System.out.println("正在吃:"+food);
}
}
### 类的定义
- 生活中描述事物无非就是描述事物的属性和行为。如:狗有颜色和年龄等属性,有跑和叫等行为。
- Java中用类Class来描述事物也是如此:
- 事物的属性:对应类中的成员属性
- 事物的行为:对应类中的成员方法
- 定义一个类,其实就是定义类中的成员(也就是成员属性和成员方法)
### 类成员的写法
- 成员变量:
- 和之前定义变量的规则是一致的
- 写在类中,成员方法的外面
- 成员方法:
- 和之前定义的方法一样
- 暂时去掉static
代码实例:
*E:\01\01 Java\day07\code\Car.java*
/*
自定义类:从具体事物到抽象概念的过程。
自定义Car类:
- 成员变量:对应的是事物的属性
- 成员方法:对应的是事物的行为(功能)
成员变量有默认值
类是一个抽象的概念,具体使用的是创建的实例(对象)。
如何从一个类创建出对象呢?
创建对象 Scanner s = new Scanner(System.in)
类型名 对象名 = new 类型名()
Car car = new car();
*/
public class Car{
String brand;
String color;
int age;
public void run(){
System.out.println("the car is running");
}
public void load(){
System.out.println("the car can load people");
}
}
*E:\01\01 Java\day07\code\CarDemo.java*
/*
演示Car类如何创建对象
成员变量:
赋值
访问
成员方法:
访问
JDK里非java.lang下的包才需要手动导入。
Scanner, Math都不需要手动导入。
*/
public class CarDemo{
public static void main(String[] args){
Car car=new Car();
//对成员属性进行赋值
car.brand="BMW";
car.color="White";
car.age=2;
//对成员属性进行访问
System.out.println(car.brand); //默认值是null
System.out.println(car.color); //默认值是null
System.out.println(car.age); //默认值是0
//访问成员方法
car.run();
car.load();
}
}
2018-08-18_162923.png
作业:自定义一个Laptop类。
*E:\01\01 Java\day07\code\Laptop2.java*
/*
自定义Laptop类
成员变量:
品牌
尺寸
价格
成员方法:
放电影
放音乐
*/
public class Laptop2{
//成员变量
String brand;
double size;
int price;
//成员方法
public void playMovie(String name){
System.out.println("正在播放电影:" + name);
}
public void playMusic(String name){
System.out.println("正在播放音乐:" + name);
}
}
*E:\01\01 Java\day07\code\Laptop2Demo.java*
/*
Laptop类测试
*/
public class Laptop2Demo{
public static void main(String[] args){
Laptop2 pc1 = new Laptop2();
//对成员变量赋值
pc1.brand="联想";
pc1.size=15.6;
pc1.price=5000;
//成员方法调用
System.out.println(pc1.brand);
System.out.println(pc1.size);
System.out.println(pc1.price);
Laptop2 pc2 = new Laptop2();
//对成员变量赋值
pc2.brand="DELL";
pc2.size=18.6;
pc2.price=6000;
//成员方法调用
System.out.println(pc2.brand);
System.out.println(pc2.size);
System.out.println(pc2.price);
//创建自定义类型的变量,指向已经存在的对象
Laptop2 pc3 = pc1;
//成员方法调用
System.out.println(pc3.brand);
System.out.println(pc3.size);
System.out.println(pc3.price);
}
}
效果如图:
2018-08-18_173027.png
### 成员变量和局部变量的区别
- 成员变量
- 成员变量定义在类中,作用范围是整个类
- 实例变量随着对象的创建而存在,随着对象而消失
- 存在于对象所在的"堆内存"中
- 成员变量有默认初始化值(0,0.0,false,null)
- 局部变量
- 局部变量定义在局部范围内:方法内、形参上、代码块内
- 局部变量存在于"栈内存"中
- 方法或者语句执行完,变量空间自动释放
- 局部变量没有默认初始值,除了形参,使用之前必须赋值
### 对象内存图
oop-06.jpg
### 形式参数的问题
Java中参数传递的原则:值传递
- 基本数据类型
- 数组
- 自定义类
举例形参是一个对象的例子:
*E:\01\01 Java\day07\code\Student.java*
/*
自定义Student类
*/
public class Student{
String name;
int age;
String sex;
public void study(){
System.out.println("正在学习");
}
}
*E:\01\01 Java\day07\code\StudentTest.java*
/*
此类用于对Student的实例进行测试
调用实例的一个方法
*/
public class StudentTest{
public void test(Student s){
//调用形参对象的方法
s.study();
}
}
*E:\01\01 Java\day07\code\StudentDemo.java*
/*
对形参是自定义类型的方法进行测试
*/
public class StudentDemo{
public static void main(String[] args){
//创建测试类的对象
StudentTest st = new StudentTest();
//创建学生对象,当成参数传给test方法
Student s = new Student();
//调用test方法
st.test(s);
}
}
效果如图:
2018-08-18_180418.png
### 匿名对象
匿名对象是对象的简写形式。
例子:
new StudentTest().test(new Student());
匿名对象两种使用情况:
- 对象方法仅进行一次调用时,多次调用不适合
- 匿名对象可以作为实际参数进行传递
使用匿名对象的好处:
- 简化写法,不必定义变量接住对象引用
- 对象用完之后就编程垃圾内存,可以被垃圾回收器回收(一个对象,只要有变量指向它,就不能被垃圾回收器回收)
## 封装
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式,面向对象的三大特点之一,其余两个:继承和多态。
好处:
- 防止数据被任意篡改,提高安全性
- 隐藏了实现细节,仅暴露方法
如何实现封装:
- 使用private关键字修饰成员变量
- 对外提供公有的setter和getter方法
private关键字:
- 是一个权限修饰符
- 用于修饰成员(成员变量和成员方法)
- 被私有化的成员只能在本类中直接访问
oop-07.jpg
常见方法:
- 将成员变量设为私有,这样就防止在类外进行访问,对外提供相应的公有的setxxx和getxxx方法
- 好处是提高对数据访问的安全性,并且可以增加复杂的逻辑控制
封装前,可以给对象的属性直接进行赋值操作,如图:
2018-08-20_015056.png
封装后,访问私有变量会直接报错,如图
2018-08-20_015730.png
代码实例:
*E:\01\01 Java\day07\code\Person.java*
[code]
/*
自定义Person类
*/
public class Person{
//使用关键字private进行封装
private String name;
private int age;
//定义setter方法和getter方法
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
public void setAge(int m){
age = m;
}
public int getAge(){
return age;
}
}
[/code]
*E:\01\01 Java\day07\code\PersonDemo.java*
[code]
/*
测试Person类
*/
public class PersonDemo{
public static void main(String[] args){
Person p = new Person();
//p.name="张三";
//p.age=200;
//System.out.println(p.name);
//System.out.println(p.age);
p.setName("李四");
p.setAge(80);
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
[/code]
### this关键字
作用:this代表当前正在调用的方法对象。
使用场景:
- setXxx方法中对成员变量赋值,区分成员变量和局部变量
- 构造方法互相调用:this(...); 注意:构造方法中使用this或者super调用其他构造方法必须是构造方法中的第一条语句。
- 方法中调用本类其他方法
代码实例:
*E:\01\01 Java\day07\code\ThisDemo.java*
[code]
/*
this关键字:用来区分局部变量和成员变量。使用this引用的就是成员变量。
*/
class Demo{
private int age;
public void setAge(int age){
//根据究竟原则,此时的age,都是取的局部变量,没有从参数传递过来的age
//age = age;
this.age = age;
}
public int getAge(){
return age;
}
}
public class ThisDemo{
public static void main(String[] args){
Demo d = new Demo();
d.setAge(100);
System.out.println(d.getAge()); //0
}
}
[/code]
### 构造方法
特点:
- 构造方法是一种特殊的方法,它的方法名和类名相同
- 不用定义返回值类型,不用return关键字
- 其返回值可以理解为是新创建对象的引用,不用显式定义
作用:给对象初始化,即给成员变量赋值。
注意:
- 如果没有写,系统提供一个默认(空参)构造方法,一旦定义了构造方法就不会,系统不会提供任何构造方法
- 多个构造方法是以重载的形式存在的
- 使用new Student();实际上就是在调用“空参”构造方法
代码实例:
[code]
/*
构造方法实例
*/
class People{
private int age;
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
//定义无参构造方法
public People(){
System.out.println("访问了无参构造方法");
}
//定义有参构造方法,利用了方法重载
public People(int age){
System.out.println("访问了有参构造方法");
this.age=age;
}
public void show(){
System.out.println("当前年龄是:" + age);
}
}
public class PeopleDemo{
public static void main(String[] args){
People p1 = new People();
p1.show();
People p2 = new People(10);
p2.show();
}
}
[/code]
如图:
2018-08-20_024437.png
注意:由于某些框架专门使用类的空参构造来创建对象,所以在定义类时,最好加上空参构造。
### 一个标准类的定义和使用
以Student类为例,定义标准的javabean
成员变量:自己分析,用私有修饰
成员方法:
- 空参构造方法
- 普通成员方法
- get/set方法
如果不希望单独获取成员变量的值,可以不定义setXxx方法
给成员变量赋值的方式有两种:set方法和构造方法
代码实例:
*E:\01\01 Java\day07\code\NoteBook.java*
[code]
/*
定义一个标准类:
1.私有的成员属性
2.公有的set/get方法
3.空参构造方法
*/
public class NoteBook{
private String brand;
private int price;
private double size;
public void setBrand(String brand){
this.brand=brand;
}
public String getBrand(){
return brand;
}
public void setPrice(int price){
this.price=price;
}
public int getPrice(){
return price;
}
public void setSize(double size){
this.size=size;
}
public double getSize(){
return size;
}
//空参构造
public NoteBook(){
}
//带参构造函数
public NoteBook(String brand, int price, double size){
this.brand=brand;
this.price=price;
this.size=size;
}
public void playMusic(){
System.out.println("正在播放音乐");
}
public void show(){
System.out.println(brand + "," + price + "," + size);
}
}
[/code]
*E:\01\01 Java\day07\code\NoteBookDemo.java*
[code]
/*
NoteBook测试类
*/
public class NoteBookDemo{
public static void main(String[] args){
//访问空参构造方法
NoteBook book1 = new NoteBook();
book1.setBrand("Dell");
book1.setPrice(3500);
book1.setSize(16.8);
book1.show();
book1.playMusic();
//访问带参构造方法
NoteBook book2 = new NoteBook("Lenovo", 5800, 24.8);
book2.show();
book2.playMusic();
}
}
[/code]
效果显示:
2018-08-20_144055.png
### this关键字在构造方法中的使用:
- 在某个构造方法中调用本类的其他构造方法
- 必须放在构造方法的第一句
代码实例:
[code]
public NoteBook(String brand, int price, double size){
this(brand, price);
this.size=size;
}
//this关键字的使用
public NoteBook(String brand, int price){
this(price);
this.brand=brand;
}
public NoteBook(int price){
this.price=price;
}
[/code]
### 一个对象的初始化(实例化)过程
以Student s=new Student();为例
- 加载Student.class文件到方法区;
- 在栈内存中定义变量s;
- 在堆内存中开辟空间;
- 对成员变量进行默认初始化(0,0.0,false,null)
- 对成员变量进行显式初始化(定义成员变量时赋值)
- 调用构造方法(成员变量赋值)
- 成员变量初始化完毕,将对象地址值返回给栈中的变量s
oop-08.jpg
### 练习
什么时候将变量定义为成员变量?
变量是用来描述类的,如果变量是这个类的描述信息,就定义为成员变量,否则,应该定义为方法形参或者方法体内,即:局部变量。
变量定义的原则:范围越小越好,能够被及时回收。
练习题:
1. 定义长方形类,其中包含求周长(perimeter)和面积(area)的方法。注意:由于没有必要单独获取长和宽,所以不需要定义get方法。
2. 定义员工类,自己分析有什么成员变量,包含一个显示所有成员变量的方法,然后测试。
3. 自定义计算类,提供基本的加减乘除运算,并测试。被计算的数和类之间没有从属关系,最好不要定义成员变量,而是方法的参数。
例1:
2018-08-20_203214.png
*E:\01\01 Java\day07\code\Rectangle.java*
[code]
/*
自定义长方形类
*/
public class Rectangle{
private int length;
private int width;
public void setLength(int length){
this.length=length;
}
public void setWidth(int width){
this.width=width;
}
public int getPerimeter(){
return 2*(length+width);
}
public int getArea(){
return length * width;
}
}
[/code]
*E:\01\01 Java\day07\code\RectangleDemo.java*
[code]
/*
测试长方形类
*/
import java.util.Scanner;
public class RectangleDemo{
public static void main(String[] args){
Rectangle r = new Rectangle();
Scanner s = new Scanner(System.in);
System.out.println("请输入长方形的长度:");
r.setLength(s.nextInt());
System.out.println("请输入长方形的宽度:");
r.setWidth(s.nextInt());
System.out.println("周长:"+r.getPerimeter());
System.out.println("面积:"+r.getArea());
}
}
[/code]
例2:
2018-08-20_203946.png
*E:\01\01 Java\day07\code\Employee.java*
[code]
/*
自定义员工类
*/
public class Employee{
private String name;
private String dept;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setDept(String dept){
this.dept = dept;
}
public String getDept(){
return dept;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public Employee(){}
public Employee(String name, String dept, int age){
this.name=name;
this.dept=dept;
this.age=age;
}
public void show(){
System.out.println(name + ", " + dept + ", " + age);
}
}
[/code]
*E:\01\01 Java\day07\code\EmployeeDemo.java*
[code]
/*
测试员工类
*/
public class EmployeeDemo{
public static void main(String[] args){
Employee e = new Employee("Leon", "IT", 31);
e.show();
}
}
[/code]
例3:
2018-08-20_205036.png
*E:\01\01 Java\day07\code\Calc.java*
[code]
/*
自定义两个数用于加减乘除
*/
public class Calc{
private int num1;
private int num2;
public Calc(){}
public int add(int num1, int num2){
return num1+num2;
}
public int sub(int num1, int num2){
return num1-num2;
}
public int mul(int num1, int num2){
return num1*num2;
}
public double div(int num1, int num2){
return num1/num2;
}
}
[/code]
*E:\01\01 Java\day07\code\CalcDemo.java*
[code]
/*
用于计算类的测试用例
*/
import java.util.Scanner;
public class CalcDemo{
public static void main(String[] args){
Calc c = new Calc();
Scanner s=new Scanner(System.in);
int num1;
int num2;
System.out.println("请输入第1个数:");
num1 = s.nextInt();
System.out.println("请输入第2个数:");
num2 = s.nextInt();
System.out.println("加:" + c.add(num1, num2));
System.out.println("减:" + c.sub(num1, num2));
System.out.println("乘:" + c.mul(num1, num2));
System.out.println("除:" + c.div(num1, num2));
}
}
[/code]
## static关键字
用于修饰成员(成员变量和成员方法),称为类成员。
代码实例:
2018-08-20_223134.png
*E:\01\01 Java\day07\code\StaticTest.java*
[code]
/*
演示static关键字
*/
public class StaticTest{
//修饰类成员属性
static int age;
//修饰类成员方法
public static void show(){
System.out.println("Hello");
}
}
[/code]
*E:\01\01 Java\day07\code\StaticDemo.java*
[code]
/*
测试Staic修饰关键字的使用
*/
public class StaticDemo{
public static void main(String[] args){
//使用类名直接访问类成员
StaticTest.age=10;
System.out.println(StaticTest.age);
StaticTest.show();
//使用对象来访问类成员
StaticTest st = new StaticTest();
st.age=20;
System.out.println(st.age);
st.show();
}
}
[/code]
总结:静态方法不能访问非静态成员;非静态方法则可以访问静态成员。(原因是先有静态成员)
图2018-08-20_223724.png
被修饰后的类成员有如下特点:
- 随着类的加载而加载
- 优先于对象而存在
- 被这个类的所有对象共享
- 可以直接被类名调用,也可以使用对象调用,但推荐使用类名调用,因为static修饰的成员就是类成员
使用时注意:
- 静态方法只能访问静态成员(变量,方法)
- 静态方法中不可以使用this, super关键字
- 非静态方法可以访问静态成员
图oop-09.jpg
### 演示Staic关键字的例子
图2018-08-20_224744.png
*E:\01\01 Java\day07\code\StaticTest2.java*
[code]
/*
演示Static关键字
*/
public class StaticTest2{
static String country="China";
String name;
public void show(){
System.out.println(country + " - " + name);
}
}
[/code]
*E:\01\01 Java\day07\code\StaticTest2Demo.java*
[code]
/*
演示StaticTest2类
*/
public class StaticTest2Demo{
public static void main(String[] args){
StaticTest2 st1 = new StaticTest2();
st1.name="Leon";
st1.show();
StaticTest2 st2 = new StaticTest2();
st2.name="Tom";
st2.country="USA";
st2.show();
}
}
[/code]
### 静态变量和实例变量之间的区别
- 所属不同
- 静态变量属于类,也称为类变量
- 实例变量属于对象,也称为对象(实例)变量
- 在内存中的位置不同
- 静态变量在方法区中
- 实例变量在堆内存
- 生命周期不同
- 静态变量随着类的加载而加载,随着类的消失而消失
- 实例变量随着对象的创建而存在,随着对象的消失而消失
- 调用方法不同
- 静态变量可以通过类名和对象名两种方式调用,推荐使用类名调用
- 实例变量只能使用对象名的方式调用
## main方法传参与格式说明
public static void main(String[] args){...}
- public: 修饰符,用来控制访问权限的。
- static: 修饰main方法属于类方法,通过类名直接调用,JVM不用创建对象即可直接调用。
- void: 返回值为空,main方法不用返回任何值。
- main: 默认的程序入口,不是关键字,JVM从这里开始执行。
- String[] args: 形参列表,String类型的数组。
- args: 形参名,也就数数组名。
如何使用?早期可以从键盘输入,但后面JDK1.5以后则被Scanner取代。如图所示:
图2018-08-20_231526.png
代码实例:
*E:\01\01 Java\day07\code\Args.java*
[code]
/*
试验main方法的参数传递
*/
public class Args{
public static void main(String[] args){
for(int i=0; i<args.length; i++){
System.out.println(args[i]);
}
}
}
[/code]
## 工具类的制作
1. 写一个类,包含一个方法用来遍历一维数组。
2. 使用static修饰,可以直接使用类名来调用,方法在main中调用。
3. 将这个类单独放到一个文件中,形成工具类。
4. 可以继续定义更多的关于数组的操作,获得最值等,丰富工具类。
注意:
- 编译的时候,只编译测试类即可
- 通过类名和对象名的方式都能调用,如果不想让使用对象名的方式调用的话,将构造方法私有化即可
### 制作一个工具类实例
图2018-08-20_234158.png
代码实例:
*E:\01\01 Java\day07\code\ArrayTool.java*
[code]
/*
求一个求数组的最值工具类
@author webjust
@version 1.0
*/
public class ArrayTool{
/**
* 求最大值
* @param arr 需要查找最大值的数组
* @return 返回数组的最大值
*/
public static int getMax(int[] arr){
int max = arr[0];
for(int i=1; i<arr.length; i++){
if(arr[i] > max){
max=arr[i];
}
}
return max;
}
/**
* 求最小值
* @param arr 需要查找最小值的数组
* @return 返回数组的最小值
*/
public static int getMin(int[] arr){
int min = arr[0];
for(int i=1; i<arr.length; i++){
if(arr[i] < min){
min=arr[i];
}
}
return min;
}
}
[/code]
*E:\01\01 Java\day07\code\ArrayToolTest.java*
[code]
/*
测试工具类
*/
public class ArrayToolTest{
public static void main(String[] args){
int[] arr = {1,2,4,7,9,10};
int max = ArrayTool.getMax(arr);
int min = ArrayTool.getMin(arr);
System.out.println(max + " - " + min);
}
}
[/code]
### 工具类说明书的制作
- 写一个完整的工具类,需要使用public修饰
- 类名上加@author, @version标签
- 方法上加@param, @return标签
- 注意:方法上标签中不用加类型
- 最终使用命令生成文档:`javadoc -d mydoc -version -author ArrayTool.java`
生成的doc文档如图:
2018-08-20_234459.jpg
## API文档
图2018-08-20_235711.jpg
- 在线版
- 离线版
- 关注事项
- 所在包
- 构造方法
- 形参,返回值类型
- 是否是static静态的
- 从哪个版本开始的
代码实例:
*E:\01\01 Java\day07\code\MathDemo.java*
[code]
/*
API文档的使用:Math类
*/
public class MathDemo{
public static void main(String[] args){
System.out.println(Math.max(1, 8)); //8
System.out.println(Math.min(-1, 2));//-1
}
}
[/code]
## 代码块
局部代码块:
在方法内部,用于限定变量的生命周期,及时释放内存。
构造代码块:
定义在成员位置,用于抽取不同构造方法中相同的部分。构造方法调用一次,构造代码块就执行一次。
静态代码块:
static修饰的构造代码块,用于对类进行初始化。静态代码块随着类的加载而执行,整个过程中只执行一次。
代码演示实例:
*E:\01\01 Java\day07\code\BlockDemo.java*
[code]
/*
代码块测试类
*/
class BlockTest{
static int num;
//构造代码块:在任何一个构造方法前都执行一次
{
System.out.println("欢迎来到:");
}
//静态代码块:对类进行初始化,在类开始加载时就执行,整个过程只执行一次
static {
System.out.println("静态代码块1");
}
static {
System.out.println("静态代码块2");
}
public BlockTest(){
System.out.println("欢迎来到1:");
System.out.println("无参构造方法");
}
public BlockTest(int num){
System.out.println("欢迎来到2:");
System.out.println("带参构造方法");
}
//局部代码块演示
public void show(){
{
int a=10;
}
{
int a=20;
}
}
}
public class BlockDemo{
public static void main(String[] args){
BlockTest bt1=new BlockTest();
BlockTest bt2=new BlockTest(1);
}
}
[/code]
图2018-08-21_234114.png
面试题:静态代码块,构造代码块,构造方法的执行顺序和特点?
答:静态代码块 -> 构造代码块 -> 构造方法。
## 继承 extends
- 多个类中存在多个相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只需从抽取出来的那那个类扩展(extends)即可
- 需要扩展的类称为子类,抽取出来的那个类称为父类,或者超类,或者基类
- 通过关键字 extends 让类与类之间产生继承关系
class 子类名 extends 父类{/*类体*/}
继承的出现,提高了代码的可复用性。
继承的出现让类和类之间开始产生了关系,提供了多态的前提。
代码实例:
图2018-08-22_221057.png
*E:\01\01 Java\day07\code\Stu.java*
[code]
/*
继承:继承人类
*/
class Stu extends Human{
}
[/code]
*E:\01\01 Java\day07\code\Human.java*
[code]
/*
继承:基类
*/
class Human{
public void eat(){
System.out.println("eat....");
}
public void sleep(){
System.out.println("sleep....");
}
}
[/code]
*E:\01\01 Java\day07\code\Teacher.java*
[code]
/*
继承:继承人类
*/
class Teacher extends Human{
}
[/code]
*E:\01\01 Java\day07\code\HumanDemo.java*
[code]
/*
继承的测试
*/
public class HumanDemo{
public static void main(String[] args){
Stu s = new Stu();
s.eat();
s.sleep();
Teacher t = new Teacher();
t.eat();
t.sleep();
}
}
[/code]
### 继承的特点
- Java只支持单继承,不支持多继承。一个类只能有一个父类,不能由多个父类。
- Java支持多层继承
- 定义继承需要注意,不仅要为了获得其他类的某个功能才去继承。类与类之间,需要有所属关系,即子类是父类的一种。
- 私有成员不能被继承
- 继承关系中成员变量的关系:子类中方法使用变量的“就近原则”
- 1. 现在子类方法中查找
- 2. 再在子类的成员变量中查找
- 3. 再找父类的成员变量
实例1:
图2018-08-22_221625.png
*E:\01\01 Java\day07\code\ExtendDemo.java*
[code]
/*
测试一个类只能继承一个父类
*/
class A{
}
class B{
}
class C extends A,B{
}
public class ExtendDemo{
public static void main(String[] args){
}
}
[/code]
实例2:
图2018-08-22_222217.png
*E:\01\01 Java\day07\code\ExtendDemo2.java*
[code]
/*
继承特点:不能继承私有成员
*/
class A{
private int num = 1; //num 在 A 中是 private 访问控制
//int num = 1;
}
class B extends A{
public void show(){
System.out.println(num);
}
}
public class ExtendDemo2{
public static void main(String[] args){
B b1 = new B();
b1.show();
}
}
[/code]
实例3:
图2018-08-22_222926.png
*E:\01\01 Java\day07\code*
[code]
/*
继承特性:就近原则
方法内 > 本类中 > 继承属性
*/
class A{
int num = 1;
}
class B extends A{
int num = 2;
public void show(){
int num = 3;
System.out.println(num);
}
}
public class ExtendDemo3{
public static void main(String[] args){
B b1 = new B();
b1.show(); //3
}
}
[/code]
## super 关键字
- super和this的用法类似
- this代表本类对象的引用
- super代表父类对象的内存空间的标识
### super的使用场景
子父类出现同名成员时,用super进行区分:
- super.成员属性
- super.成员方法()
子类使用super调用父类的构造方法
- super(...)
实例:
图2018-08-23_001616.png
*E:\01\01 Java\day07\code\SuperDemo.java*
[code]
/*
super关键字
*/
class A{
int num=10;
}
class B extends A{
int num=20;
public void show(){
System.out.println(num); //20
System.out.println(super.num); //10
}
}
public class SuperDemo{
public static void main(String[] args){
B b1 = new B();
b1.show();
}
}
[/code]
代码实例:
图2018-08-23_002349.png
*E:\01\01 Java\day07\code\SuperDemo2.java*
[code]
/*
Super关键字特性
*/
class A{
int num=10;
public A(){
System.out.println("父类无参构造方法");
}
public A(String s){
System.out.println(s);
System.out.println("父类带参构造方法");
}
}
class B extends A{
int num=20;
//程序会默认先执行B类的无参构造方法,然后执行父类的无参构造方法
public B(){
//必须在程序的第一行,显示的调用父类的带参的构造方法,使用Super关键字表示是父类
super("1000"); //不放在第一行,会报错
System.out.println("子类无参构造方法重写...");
}
public void show(){
System.out.println(num); //20
System.out.println(super.num); //10
}
}
public class SuperDemo2{
public static void main(String[] args){
B b1 = new B();
b1.show();
}
}
[/code]
## 方法重写 Override|Overwrite
- 子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写
- 父类中的私有方法不可以被重写
- 在子类重写方法中,继续使用被重写的方法可以使用super.方法名(...)
- 覆盖注意事项:覆盖时,子类方法权限一定要大于等于父类的方法权限;除了访问权限外,其他部分和父类保持一致;静态只能覆盖静态;
- 覆盖的应用:当子类需要父类的功能,而功能主体子类有自己的特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
代码实例:
2018-08-23_004820.png
*E:\01\01 Java\day07\code\OverrideDemo.java*
[code]
/*
方法重写实例
*/
class Father{
public void test(){
System.out.println("Father test()...");
}
public static void test2(){
System.out.println("Father test2()...");
}
void test3(){
System.out.println("Father test3()...");
}
}
class Son extends Father{
//重写父类方法,需要同名方法
public void test(){
super.test(); //在子类中调用父类的方法
System.out.println("Son test()...");
}
//重写静态方法,子类必须也是静态方法
public static void test2(){
System.out.println("Son test2()...");
}
//子类的权限范围必须大于父类的方法
public void test3(){
System.out.println("Son test3()...");
}
}
public class OverrideDemo{
public static void main(String[] args){
Son s = new Son();
s.test();
s.test2();
s.test3();
}
}
[/code]
### 子类的实例化过程
- 子类中所有的构造方法默认都会访问父类中的空参构造方法,因为每一个构造方法的第一行都有一条默认的语句super(),除非第一行是用this或super显示调用了其他的构造方法
- 子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的,也就是父类对象必需在子类对象初始化前初始化ok
- 当父类中没有空参构造方法时,子类的构造方法必须通过this或super语句指定要访问的构造方法
代码实例:
图2018-08-23_010218.png
*E:\01\01 Java\day07\code\OverrideDemo2.java*
[code]
/*
方法重写实例
*/
class Father{
public Father(){
System.out.println("Father构造方法");
}
}
class Son extends Father{
public Son(){
super(); //默认会添加父类的无参构造方法,也可以指定为其他的父类构造方法
System.out.println("Son构造方法");
}
}
public class OverrideDemo2{
public static void main(String[] args){
Son s = new Son();
}
}
[/code]
## final 关键字
final可以修饰类、方法、变量。
图oop-10.jpg
final修饰的成员变量,必须在构造方法执行完成之前初始化:
1. 可以在定义的时候就赋值(最常见)
2. 可以在构造代码块中赋值,多个构造代码块中不能重复赋值
代码实例:
图2018-08-23_012422.png
*E:\01\01 Java\day07\code\FinalDemo.java*
[code]
/*
final关键字
*/
class A{
public final int num;
}
class B extends A{
int num=20;
public void show(){
System.out.println(num);
}
}
public class FinalDemo{
public static void main(String[] args){
B b1=new B();
b1.show();
}
}
[/code]
## 抽象类
### 1. 抽象类概述
抽象定义:抽象就是从多个事物中将共性的,本质的内容抽取出来
抽象类定义:Java中可以定义没有方法体的方法,该方法的具体实现由子类来完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象方法的由来:多个对象都具备相同的功能,但是功能的具体实现有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。
> 如果在父类中定义了一个各子类都有不同实现的方法,那么子类需要重写这个方法,为什么父类还要定义这个方法?这是为多态做准备。
由于多态的要求:父类引用指向子类实例,但父类不能调用子类独有的方法,为了使用多态,父类中必须定义这个方法,但是方法的实现又会被子类重写。
为了既能使用多态,又能省去在父类中的麻烦,Java提供了抽象方法,在父类中只定义方法名,不实现方法体。
图oop-10.jpg
### 2. 抽象类特点
抽象类和抽象方法必须用abstract关键字来修饰。
抽象方法只有方法声明,没有方法体,定义在抽象类中。
抽象类不可以被实例化,原因如下:
- 抽象类是具体事物抽象出来的,本身不是具体的,没有对应的实例。
- 假设抽象类可以创建对象,那么调用其抽象方法,将无意义。
抽象类通过其子类实例化,而子类需要实现抽象类中所有的抽象方法后才能创建对象,否则该子类也是抽象类。
修饰符 abstract 返回值类型 方法名(参数列表);
图oop-11.jpg
### 3. 抽象类举例代码
子类Dog, Cat分别实现Animal类的抽象方法eat()。
图oop-12.jpg
2个代码实例
### 4. 抽象类相关问题
- 抽象类是否可以有构造方法?
抽象类中可以有成员变量,而构造方法的作用就是对成员变量初始化的,所以可以有构造方法。
- 抽象关键字abstract不可以和哪些关键字共存?
static, final, private
- 抽象类中可不可以没有抽象方法?
可以,比如可以不让外界实例化。
## 接口
类可以继承一个类的同时实现多个接口
class Dog extends Animal implements CanJump,CanWin{
...
}
接口与接口之间是继承关系
public interface InterfaceA{void m1();}
public interface InterfaceB{void m2();}
public interface InterfaceC extends InterfaceA,InterfaceB{
//有两个抽象方法继承至父类接口;
}
接口是抽象的一种机制。
图oop-13.jpg
现实事物的共性抽取成类:
将不同类中的同名方法抽象成抽象方法,形成抽象类。
对类的成员变量和方法继续抽象的话,就形成接口,接口是类的最高级别的抽象机制。
接口中只有常量和抽象方法
接口是一种规范,实现一个接口,就需要实现其中所有的抽象方法。
格式:
[code]
[public] interface 接口名 [extends 父接口1,父接口2...]{
//成员常量: public static final
//抽象方法: public abstract
}
[/code]
- public: 可以在所有包中访问,不加的话,表明只能在本包中访问该接口
- 接口中的成员修饰符是固定的,写与不写都有
- 成员常量: public static final
- 抽象方法: public abstract
- 接口中的成员都是public修饰的。
- 接口的出现将"多继承"通过另外一种形式体现出来,即"多实现"。
### 接口的特点
接口是程序的扩展功能:想给类A增加一个功能,只需要定义一个接口,然后自定义一个类,继承自类A,并实现这个接口,即可完成把类A的功能扩展。
图
图oop-13.jpg
图2018-08-24_110655.png
*E:\01\01 Java\day07\code\InterfaceDemo.java*
[code]
/*
接口:对功能的扩展
*/
class Dog{
public void eat(){
System.out.println("Dog eat()....");
}
}
interface Jump{
public abstract void jump();
}
class JumpDog extends Dog implements Jump{
public void jump(){
System.out.println("Dog jump()....");
}
}
public class InterfaceDemo {
public static void main(String[] args){
JumpDog jd = new JumpDog();
jd.eat();
jd.jump();
}
}
[/code]
- 接口的出现降低耦合性
接口只定义规范,不关心实现,这是设计和实现相分离的表现。
- 接口可以用来多实现
[code]
class SuperMan implements CanFly,CanSwin,CanFight{
//实现在所有接口中定义的抽象方法
}
[/code]