💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 大数据学习笔记第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]