面向对象
1类和对象的理解
1类:
Java中用class描述类
成员变量表示就是事物的属性
成员方法表示就是事物的行为
一个类组成
类的组成
包的声明 package;
包的导入 import;
[访问权限修饰符] class 类名 extends 父类(普通类,抽象类) implements 接口 {
成员变量
常量
字面值常量
自定义常量
类变量
构造代码块
构造方法
无参构造方法
全参构造方法
成员方法
普通方法
get/set读写方法
抽象方法
类方法
内部类
成员内部类
静态内部类
局部内部类
匿名内部类
内部接口
静态代码块
局部代码块
}
2对象:
1.对象一定是客观存在的,是具体的
2.万事万物都是对象
3.对象是在类的基础上创建的
4.对象又称为 实例/引用/变量/对象
3格式:
[访问权限修饰符] class 类名{
成员变量
成员方法
}
如何创建一个对象:
类名 对象名 = new 类名();
如何访问一个对象:
访问对象的属性:对象名.属性
访问对象的方法:对象名.方法名()
public class OOPDemo01 {
public static void main(String[] args) {
// 如何创建一个对象
Student 小丽 = new Student();
小丽.name = "小丽丽";
小丽.age = 18;
小丽.gender = "女";
小丽.isMarried = false;
System.out.println(小丽.name);
System.out.println(小丽.age);
System.out.println(小丽.gender);
System.out.println(小丽.isMarried);
int result = 小丽.add(10, 20);
System.out.println(result);
小丽.eat();
System.gc();
System.runFinalization();
}
}
/*
* 1. 定义老师类
* 2. 创建老师对象
* 3. 访问对象成员
* 4. 画出内存图
*/
// 类的定义
// 定义一个学生类
class Student {
// 成员变量
// 身份证
String id;
// 姓名
String name;
// 年龄
int age;
// 性别
String gender;
// 身高
double height;
// 是否结婚
boolean isMarried;
// 成员方法: 去掉static
public int add(int a, int b) {
return a + b;
}
// 吃饭
public void eat() {
System.out.println("好吃");
}
// 学习
public void study() {
System.out.println("活到老学到老!!!");
}
}
2成员变量和局部变量的区别
1.定义的位置不一样
员变量: 定义在类体以内,方法体以外
局部变量: 方法的声明上(形参),或者方法体内
2.内存的位置不一样
成员变量: 在堆区分配内存空间
局部变量: 在栈区分配内存空间
3.初始值不一样
成员变量: 成员变量默认有初始值,系统会赋予如下初始值
byte short int long 0
float double 0.0
boolean false
char \u0000
引用类型 null
局部变量: 默认没有初始值
4.生命周期不一样
生命周期: 创建到销毁的全过程
成员变量: 随着对象的创建而创建,随着对象的销毁而销毁,对象成为垃圾对象的时候并且垃圾回收器空闲的时候销毁
局部变量: 随着方法的调用而创建,随着方法的销毁而销毁
5.当成员变量和局部变量重名的时候,局部变量优先(就近原则)
public class OOPDemo02 {
public static void main(String[] args) {
Person p = new Person();
p.method(100);
}
}
class Person {
// 成员位置
int age;
int num;
public void method(int n) {
// 局部位置
int num = 10;
System.out.println(height);
System.out.println(num);
}
double height;
}
3匿名对象
1匿名对象本质:
就是没有名字的对象
2特点:
1.匿名对象没有名字
2.匿名对象只能够调用一次
3.由于匿名对象使用完毕之后立刻就成为垃圾对象,等待垃圾回收器在空闲的时候回收垃圾
4.一般可以节约内存资源,在Java中应用少,在Android应用多
5.可以用简化编程[参数传递]
3 垃圾对象?
1.匿名对象
2.对象被赋值为null
3.对象所在的方法调用完毕
4.当主方法调用完毕
5.当对象被重新new
public class OOPDemo06 {
public static void main(String[] args) {
new Student().name = "隔壁老王";
int result = new Student().add(10, 20);
System.out.println(result);
System.out.println(new Student().name); // null
System.out.println("=========1.匿名对象=============");
method(new Student());
System.out.println("您输入的数是: " + new Scanner(System.in).nextInt());
System.out.println("=======2.对象被赋值为null========");
Student s1 = new Student();
// s1 = null;
System.out.println("=========3.对象所在的方法调用完毕=============");
show();
System.out.println("=========4.当主方法调用完毕=============");
System.out.println("==========5.当对象被重新new=================");
s1 = new Student();
}
public static void show() {
Student s = new Student();
s.eat();
}
public static void method(Student s) {
System.out.println(s.add(10, 20));
}
}
4构造方法
1构造方法本质:
就是一个方法而已,只不过能够帮助我们初始化成员
2构造方法好处:
1.能够快速初始化成员
2.能够在堆区为对象分配空间
3 给类成员的3种初始化
1.通过set/get方法
2.通过构造方法
3.通过自定义方法
4类的组成
class 类名 {
成员变量 【封装】
构造方法
无参构造方法
全参构造方法
成员方法
普通方法
get/set方法【成员的读写方法】
5构造方法的特点:
1.构造方法没有返回值,连void都没有
2.构造方法的方法名必须和类名保持一致
3.构造方法用来给成员快速初始化
4.如果你没有书写无参构造方法,系统会默认帮我们编写 一个无参构造方法
5.如果你一旦编写一个带参构造方法,就会覆盖系统写的 无参构造方法
6.养成一个好的习惯: 书写每个类都必须显示定义无参构造方法
无参构造方法的意义:
1.为一些不需要外界传入的成员赋值,同时会在对象创建的时候初始化
2.如果你一旦编写一个带参构造方法,就会 覆盖系统写的无参构造方法,当你再使用无参构造 方法的时候会编译报错
3.反射中大部分都是创建无参构造方法,很多框架都是利用反射,所以如果你不提供无参构造方法,很可能框架报错
7.构造方法本质还是方法,可以方法重载
8.this除了可以访问本类的成员变量和成员方法,还能够访问本类的构造方法,this访问构造方法必须出现在构造器的第一句
9.访问构造方法的同时一定会分配内存空间吗? -- 不一定
访问构造方法的方式有3种
1.new关键字 会创建对象
2.this关键字 不会创建对象,就类似访问普通方法一样
3.super关键字 不会创建对象,后面讲解
10.构造方法不可以递归使用
11.构造方法之间不能够嵌套调用
5封装
1 封装
类的理解: 组织代码(方法),封装数据(成员变量): 提高数据的安全性
为什么需要学习封装
学生的年龄可以改为非法数值? -- 判断非法数据,数据校验
学生的年龄不希望外界随意访问? -- 数据的安全性
注意: 在类体以内方法体以外不能够编写逻辑业务代码,只能够写在方法的内部
private关键字: 它本质是一个访问权限修饰符,用来放置外界访问成员变量
2封装的步骤
1.给需要封装的成员加上访问权限修饰符(private) -- 防止外界直接通过.直接访问成员
2.对外界提供公共的访问方法(set/get方法) -- 提供外界对内部成员的访问通道
3.在读写方法中添加数据校验代码 -- 可以对成员的访问提供数据控制 (看需求可选择)
自动生成get/set方法 alt + shift + s 再按 R
封装一个苹果手机类
public class PrivateDemo02 {
public static void main(String[] args) {
ApplePhone phone7 = new ApplePhone();
phone7.setBrand("苹果7plus");
phone7.setColor("中国红");
phone7.setPrice(8000.0);
System.out.format("手机幸好是%s,手机颜色是%s,手机价格是%f", phone7.getBrand(),phone7.getColor(),phone7.getPrice());
}
}
class ApplePhone {
private String brand;
private String color;
private double price;
public void setBrand(String brand) {
this.brand = brand;
}
public String getBrand() {
return brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
6 this关键字
this本质就是一个变量/地址/实例/引用,表示当前对象
this理解
1.成员变量和局部变量重名的时候使用this
2.创建任何一个对象,都会默认创建一个this变量指向同一个堆区的空间
3.this的设计理念代表代词 我的 概念,只能够出现在类的内部
4.this可以完全理解为一个引用,一个本类的实例,或者本类的对象
5.默认访问内部类的成员变量或者成员方法,都会省略一个this
6.this本身就代指当前对象,谁调用this就是谁
public class ThisDemo {
public static void main(String[] args) {
Car bmw = new Car();
Car benz = new Car();
benz.setBrand("奔驰");
bmw.setBrand("宝马车");
bmw.setColor("金色");
bmw.setPrice(140.0);
String brand = bmw.getBrand();
String color = bmw.getColor();
double price = bmw.getPrice();
System.out.println(brand + "," + color + "," + price);
System.out.println("========================");
benz.show();
bmw.show();
}
}
class Car {
private String brand;
private String color;
private double price;
public void show() {
method(this);
}
public void method(Car c) {
System.out.println(c.getBrand());
}
public String getBrand() {
String c = this.getColor();
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
7 static
1 为什么需要学习static?
静态变量,为所有对象共享使用
2 static概述 : 静态的 / 共享的
3static的特点
1.静态变量属于类,而不属于具体的对象
2.在静态环境中,只能够访问静态变量,不能够访问非静态变量
3.在非静态环境下,既能够访问静态变量,也能够访问非静态变量
4.静态变量的访问格式:
1.通过对象访问 s1.country
2.通过类名访问 Student.country
3.通过set方法访问 setCountry
5.当有一个对象修改了static属性的值,任意一个对象都会被影响
6.静态修饰方法或者变量有意义吗?
变量: 共享
方法: 方法是用来完成特定功能的,给别人调用,而静态修饰的方法正好可以通过类名直接访问
方便调用,不需要创建对象,常用来编写工具类 Math
书写工具类的步骤
1.构造方法私有
2.方法成员全部静态化
7.static环境中不能够出现this和super
8.static不仅可以修饰变量和方法,还能够修饰代码块,以及类(保证是内部类)
面试题:
实例变量和类变量的区别?
8 代码块
1代码块: 大括号包裹的就称为代码块
2 代码块的分类:
1.局部代码块: 定义在局部位置,限定局部变量的作用域
2.构造代码块: 定义在成员位置,用来抽取多个构造方法中的重复代码,可以简化编程
在构造方法之前执行,并且每次创建对 象前都会执行一次,优于构造方法之前执行
3.静态代码块:
定义在成员位置,在构造代码块和构造方法之前执行,并且只会执行一次
作用:
a.用来加载一些预定义的资源,比如说数据库的连接,配置文件的读取
b.用来初始化静态成员
调用时机: 在类加载的时候执行
3 执行时机和执行顺序:
静态代码块 > 构造代码块 > 构造方法
构造方法在对象创建的时候执行,可以执行多次
构造代码块在对象创建之前执行,可以执行多次
静态代码块在类第一次出现(类加载)/或者第一次使用的时候执行,只能够执行一次
JVM启动,jvm.main();
1 加载 StaticDemo03.class 装载入内存
StaticDemo03 的静态代码块01
StaticDemo03 的静态代码块02
2.执行主方法:
执行第一行代码,CodeBlock类会被加载, 装 载入内存
CodeBlock 静态代码块1
CodeBlock 静态代码块2
3.执行构造方法
CodeBlock 构造代码块1
CodeBlock 构造代码块2
CodeBlock无参构造方法
CodeBlock 构造代码块1
CodeBlock 构造代码块2
CodeBlock无参构造方法
public class StaticDemo03 {
public static void main(String[] args) {
CodeBlock.country = "中国";
CodeBlock cb = new CodeBlock();
CodeBlock cb2 = new CodeBlock();
}
static {
System.out.println("StaticDemo03 的静态代码块01");
}
static {
System.out.println("StaticDemo03 的静态代码块02");
}
}
class CodeBlock {
static String country;
static {
System.out.println("CodeBlock 静态代码块1");
}
{
System.out.println("CodeBlock 构造代码块1");
}
static {
System.out.println("CodeBlock 静态代码块2");
}
{
System.out.println("CodeBlock 构造代码块2");
}
public CodeBlock() {
System.out.println("CodeBlock无参构造方法");
}
}
4.同步代码块(后面讲解多线程的时候讲解)
public class StaticDemo02 {
public static void main(String[] args) {
Code c1 = new Code();
Code c2 = new Code(10);
Code c3 = new Code(10, 20);
}
{
}
}
class Code {
int num1;
int num2;
static int num3;
static {
num3 = 10;
System.out.println("我是静态代码块");
}
// 构造代码块
{
System.out.println("我是构造代码块");
}
public Code() {
// System.out.println(calculate());
System.out.println("我是构造方法1");
synchronized (new Object()) {
}
}
public Code(int num1) {
// System.out.println(calculate());
System.out.println("我是构造方法2");
this.num1 = num1;
}
public Code(int num1, int num2) {
// System.out.println(calculate());
System.out.println("我是构造方法3");
this.num1 = num1;
this.num2 = num2;
}
public int calculate() {
return num1 + num2;
}
public void show() {
// 局部代码块
{
int i = 20;
}
int i = 10;
System.out.println(i);
}
}
9包 package
1为什么需要有包?
1.处理同一个中的重名问题
2.分门别类地管理Java文件
3包的概述: 本质就是文件夹
4 包的声明
格式:
package 包名1.包名2...包名n; 告诉计算机当前的这个Java类在哪个包下..
包的使用:
import java.util.Scanner; (类的全路径)
import java.util.*;
2 包的特点:
1.同包下的类相互调用不需要导包
2.在同一个类中同时使用不同包的同名类,需要显示导入,例如后面学习数据库的时候 Java中的日期类:java.util.Date 数据库中的日期类: java.sql.Date
将数据库中的日期转换成Java的日期
3.如果一个类没有包,那么该类就成为了孤立的类,不能够被别人使用
4.包的声明必须出现在类的第一句
5.如果希望导入一个包中所有的类: import x xx.xxx.xx.*
6.某些包中的类是不需要我们导入的,系统会自动导入 例如lang(Java的核心包)包
7.在自己写一个类的时候千万不要和系统类重名
3 JRE中提供给我们常见的包:
ava.lang 包含了Java中的一些核心类 String Object Math Arrays
java.awt: 包含了Java的一些窗口相关类
java.util 工具包
java.io 输出输出流相关的类
java.net 网络包
java.reflect: 反射包
4 快捷键: 一键导包 Ctrl + Shift + O
10 extends
1 概念
之前我们了解到类是对对象的抽象,具有共同属性和行为的许多对象抽象出一个类。
例如:有三个学生小明,小红,小李都有姓名,年龄,身高,体重,都会吃,睡,学习等等,
我们可以将这些属性和行为抽象出来成为一个类,也就是学生类。
假设多个类存在相同属性和行为时,我们同样可以将这些内容抽取到单独的一个类中,那么这多个类没有必要再定义这些属性和行为,只需要继承这个单独的类就好了,这就是继承。
例如:小学生,中学生,大学生,他们都有姓名,年龄,学号的属性,交学费的行为,我们可以把这些相同的属性和行为抽取出来放到单独的一个学生类中,
小学生,中学生,大学生只需要继承学生类就好了,这样大大减少了代码量。
总结:类是对对象的抽象,继承是对某一批类的抽象。
2 为什么需要学习继承? (简化 可读性 可维护性 可扩展性 健壮性 安全性 效率)
1.简化了子类代码
2.提高了可维护性和维护性
3.让类与类产生了关系,这是多态的必要条件(后面讲解)
3 格式:
通过extends关键字可以实现类与类的继承
class 子类类名 extends 父类类名 {}
被继承的这个类称为父类,基类或者超类
继承的这个类称为子类或者派生类
4继承的缺点:
1.耦合性提高,让类与类之间产生了关系,容易牵一发动全身
2.继承要慎用,降低了扩展性和维护性
5什么时候使用继承?
1.一般在编写代码的时候出现了重复代码,我们可以将重复代码向上抽取成为一个父类 BaseAcitvity BaseDao
2.当某个类的设计非常复杂的时候,可以使用继承
6继承的注意事项
1.单一继承性
2.支持多层继承 class C extends B {} class B extends A{}
3.父类成员使用private修饰,理解为不能继承,所以父类成员一般都会用public 或者 protected修饰
4.如果一个子类继承了父类的属性和方法,同时还可以有自己的方法
5.当子类成员和父类成员重名的时候,子类优先
6.子类不能够继承父类的构造方法,但是子类可以访问父类构造方法
7使用继承的步骤:
1.先书写子类代码
2.观察多个类中是否存在共性代码
3.将共性代码抽取放在一个类中
4.让子类继承父类
5.将子类代码删除
6.验证是否满足 A is a B 的条件
public class ExtendsDemo {
public static void main(String[] args) {
// PrimaryStudent ps = new PrimaryStudent();
// ps.name = "小李";
// ps.payMoney(10000);
//
// MiddleStudent ms = new MiddleStudent();
// ms.name = "中李";
// ms.payMoney(5000.0);
//
// BigStudent bg = new BigStudent();
//
// ps.talkLove();
// ms.talkLove();
// bg.talkLove();
//
// System.out.println(ps.money);
// System.out.println(ms.money);
//
// MyFrame frame = new MyFrame();
// frame.setSize(500,500);
// frame.setTitle("Flappy Bird");
// frame.setLocationRelativeTo(null);
// frame.setVisible(true);
PrimaryStudent ps = new PrimaryStudent();
System.out.println(ps.name);
}
}
class MyFrame extends JFrame {}
class Student {
String name = "学生";
private int age;
double money;
public Student() {
}
public Student(String name, int age, double money) {
this.name = name;
this.age = age;
this.money = money;
}
public void payMoney(double money) {
this.money = money;
}
public void study() {
System.out.println("好好学习");
}
}
class PrimaryStudent extends Student{
String name = "小学生";
public void talkLove() {
System.out.println("过家家");
}
}
class MiddleStudent extends Student{
public void talkLove() {
System.out.println("手拉手");
}
}
class BigStudent extends Student{
public void talkLove() {
System.out.println("看电影");
}
}
11 super和this关键字
1 this和super的区别?
this可以理解为当前对象,谁调用this就代指那个 调用者对象,每次创建一个对象,默认都会创建一个t his变量指向同一个堆区空间
super本质是父类存储空间的标识,但是你可以理解为父类对象,那么同样可以使用super这个关键字来访问父类的成员
2 访问成员
成员变量
this.成员变量
super.成员变量
成员方法
this.成员方法
super.成员方法
构造方法
this(参数列表)
super(参数列表)
3注意:
1.任何一个类的无参或者带参构造方法的第一句都默认省略 super();
2.任何一个类都直接或者间接继承自Object类
3.为什么设计Object类,满足了万事万物皆对象的原则
访问根类Object的无参构造方法的意义何在? - 加载Object类中静态成员和静态代码块
4.创建一个子类对象默认会去调用父类的构造方法,那么有没有在创建子类对象的同时也创建了父类对象呢?
-- 没有创建父类对象,本质就相当于访问了一个普通方法,创建对象开启空间需要new关键字
5.super访问父类带参构造方法的意义? -- 帮助子类初始化父类继承下来的成员,简化编程
6.静态环境/静态上下文(static修饰的方法),不能够出现this和super
7.super必须出现在构造方法的第一句
8.this和super不能够共存
4 类加载时机:
1.第一次出现
2.成员第一次被访问
public class SuperDemo01 {
public static void main(String[] args) {
// Zi zi = new Zi();
Zi zi2 = new Zi("张三", 15, "北京西路");
// zi.method();
}
}
class Fu extends Object {
public String name = "FuName";
public int age;
public Fu() {
super();
System.out.println("我是父类无参构造方法");
}
public Fu(String name, int age) {
System.out.println("我是父类带参构造方法");
this.name = name;
this.age = age;
}
public void fuMethod() {
System.out.println("Fu.fuMethod()");
}
}
class Zi extends Fu{
private String name = "ZiName";
private String address;
public Zi() {
super();// this();
System.out.println("我是子类无参构造方法");
}
public Zi(String name, int age, String address) {
// super();
// System.out.println("我是子类带参构造方法");
// this.name = name;
// this.age = age;
super(name, age);
this.address = address;
}
public Zi(String name) {
super();
this.name = name;
}
public void method() {
System.out.println("this:" + name);
System.out.println("super:" + super.name);
super.fuMethod();
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
12 访问权限修饰符
1 访问权限修饰符的特点:
1.public对一切类可见
2.protected对同包下和不同包的子类可见
3.default仅对同包下可见
4.private仅对本类可见
5.访问权限修饰符有四个,访问权限修饰符可以修饰成员变量,成员方法,还可以修饰类(只有public可以)
在这里需要注意,其实protected/private也可以修饰类,但是必须是内部类
6.访问权限修饰符的宽严关系
public > protected > default > private
7.Java当中封装就是通过访问权限修饰符来实现的
封装: 就是屏蔽内部的成员,而屏蔽内部的成员是通过访问权限修饰符来控制的
2 访问权限修饰符:(如图)
public protected default[只是一种默认状态] private
概述: 本质就是用来控制类中成员的访问权限,封装的核心
3 类的本质就是组织代码,封装数据
13方法重写
1为什么需要方法重写?
当父类的方法不能够满足子类的需求的时候,需要方法重写
2 方法重写的特点:
1.发生在具有子父类的两个类中
2.方法名相同
3.参数列表完全相同
4.返回值类型可以相同或者是其子类
5.访问权限修饰符不能够严于父类
3 注意事项:
1.private修饰的方法不能够被继承,所以不能够被重写
2.构造方法不能够被重写
3.子类重写父类的方法时,访问权限修饰符不能更低,严格
4.返回值类型相同或者是其子类
5.重载和重写的区别
/*
*有农民(farmer),教师(teacher),科学家(scientist),服务生(attendant),
* 其中农民,服务生只有基本工资,教师除基本工资外,还有课酬(元/天),科学家除基本工资外,还有年终奖,请你写出相关类,
* 将各种类型的员工的全年工资打印出来。
*
* OOA: 分析有多少个对象,有多少个对象就写多少个类,分析每个类的成员
* OOD: 分析对象与对象之间的关系,继承关系
* OOP: 在主方法尽量出现创建对象,调成员
*/
public class OverrideDemo02 {
public static void main(String[] args) {
Farmer f = new Farmer(3000.0);
Teacher t = new Teacher(5000, 80000);
Scientist s = new Scientist(8000, 100000);
Attendant a = new Attendant(2100);
f.printAllYearSalary();
t.printAllYearSalary();
s.printAllYearSalary();
a.printAllYearSalary();
}
}
class Worker {
public double basicSalary;
public Worker() {
super();
}
public Worker(double basicSalary) {
super();
this.basicSalary = basicSalary;
}
public void printAllYearSalary() {
System.out.println("全年工资: " + (basicSalary * 12));
}
public double getBasicSalary() {
return basicSalary;
}
public void setBasicSalary(double basicSalary) {
this.basicSalary = basicSalary;
}
}
class Farmer extends Worker {
public Farmer() {}
public Farmer(double basicSalary) {
super(basicSalary);
}
}
class Teacher extends Worker {
private double courseFee;
public Teacher() {
super();
}
public Teacher(double basicSalary, double courseFee) {
super(basicSalary);
this.courseFee = courseFee;
}
@Override
public void printAllYearSalary() {
System.out.println("全年工资: " + (basicSalary * 12 + courseFee));
}
public double getCourseFee() {
return courseFee;
}
public void setCourseFee(double courseFee) {
this.courseFee = courseFee;
}
}
class Scientist extends Worker {
private double yearEndBonus;
public Scientist() {
super();
}
public Scientist(double basicSalary, double yearEndBonus) {
super(basicSalary);
this.yearEndBonus = yearEndBonus;
}
@Override
public void printAllYearSalary() {
System.out.println("全年工资: " + (basicSalary * 12 + yearEndBonus));
}
public double getYearEndBonus() {
return yearEndBonus;
}
public void setYearEndBonus(double yearEndBonus) {
this.yearEndBonus = yearEndBonus;
}
}
class Attendant extends Worker {
public Attendant() {}
public Attendant(double basicSalary) {
super(basicSalary);
}
}
14 final关键字
1 为什么需要final关键字?
1.便于维护和修改
2.提高了程序的可读性
3.用于对Java中某些元素加锁
2 常量存在于方法区
常量分类
字面值常量 10 10.5 false null
自定义常量 final修饰的
3 final: 不变的,最终的
final
修饰变量 成为常量,不能够被二次赋值
局部变量
可以暂时不给出初始值
只能赋值一次
成员变量
在声明的时候必须给出初始值
修饰方法不能够被子类重写
修饰类不能够被子类继承
jdk1.8以前 **内部类** 访问外面必须得final修饰才行 否则不能够访问
String类是final修饰的
public class FinalDemo01 {
public static final int NUM = 10;
public static void main(String[] args) {
final int NUM2;
NUM2 = 20;
System.out.println(NUM2);
// NUM = 20;
// System.out.println(NUM);
// NUM = 20;
// System.out.println(Math.PI);
//
// int state = 0X0001;
// switch (state) {
// case GameState.START:
// System.out.println("游戏开始的代码");
// break;
// case 0X0002:
// System.out.println("游戏结束的代码");
// break;
// case GameState.RUNNING:
// System.out.println("游戏运行的代码");
// break;
// default:
// break;
// }
}
}
/*final*/ class PerfectClass {
public final void perfectMethod() {
System.out.println("GameState.perfectMethod()");
// String
}
}
class MyPerfectClass extends PerfectClass {
// @Override
// public void perfectMethod() {
// // TODO Auto-generated method stub
// super.perfectMethod();
// }
}
class GameState {
public static final int START = 0X0001;
public static final int END = 0X0002;
public static final int RUNNING = 0X0003;
}
15 多态
1多态概述:
生活中: 同一个动作在不同的环境下体现出来的不同的状态
Java中: 同一个方法在不同的类下体现出来的不同的实现
内存中: 父类引用指向子类对象
2多态的必要条件:
1.必须存在继承关系
2.必须存在方法重写
3.必须存在父类引用指向子类对象
3 利用多态访问成员的特点:
成员变量
编译时期看左边类型,运行时期看左边类型
成员方法
编译时期看左边类型,运行时期看右边类型
构造方法
先访问父类构造方法,帮助子类初始化父类继承过来的成员
静态方法
编译时期看左边类型,运行时期看左边类型
4 多态的优点:
1.简化了代码
2.提高了维护性和扩展性(开闭原则: 对扩展开放,对修改关闭)
5 多态的缺点:
父类引用无法访问子类所特有的方法(解决办法: 向下转型)
6向上转型(自动转换)
**格式:<父类型> <引用变量名> = new <子类型>();
** 特点:
子类转为父类 父类的引用指向子类对象。自动进行类型转换
此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法
此时通过父类引用变量无法调用子类特有的属性和方法
7 向下转型(强制转换)
**格式:<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
**特点:
父类转为子类,父类引用转为子类对象。强制类型转换
在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常
向下转型能够解决父类引用无法调用子类所特有方法的问题,但是在转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常
**解决办法: 在向下转型的过程中,对父类所对应的每一个子类做逐一判断
**结合 instanceof 关键字
测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据
记住:instanceof通常和向下转型(强制类型转换)结合使用
**如果新增加一个子类我们必须继续添加条件判断
如果父类是 Object
由于父类Object有无数个子类,逐一判断总会有安全隐患,这个问题没办法解决,但是可以换一个方式执行
泛型: (后面讲解)
public class DuoTaiDemo03 {
public static void main(String[] args) {
Car c = new BMW();
// c = new Benz();
// c.run();
// 利用父类引用访问子类所特有的方法
// c.showMoney();
// 格式:<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
// BMW bmw = (BMW) c;
// bmw.showMoney();
// Benz benz = (Benz) c;
// benz.showSpeed();
// System.out.println(c instanceof Car);
// System.out.println(c instanceof BMW);
// System.out.println(c instanceof Benz);
c = new Benz();
// 调用重写的方法
c.run();
// 调用子类所特有的方法
if (c instanceof BMW) {
BMW bmw = (BMW) c;
bmw.showMoney();
} else if(c instanceof Benz) {
Benz benz = (Benz) c;
benz.showSpeed();
} else if (c instanceof MSLD) {
// ...
}
}
}
class Car {
public void run() {
System.out.println("我自己都不知道怎么跑你还问我?");
}
}
class BMW extends Car {
@Override
public void run() {
System.out.println("嗖嗖地跑");
}
public void showMoney() {
System.out.println("我不差钱儿!!!");
}
}
class Benz extends Car {
@Override
public void run() {
System.out.println("吱吱地跑");
}
public void showSpeed() {
System.out.println("法拉利比我慢!!!");
}
}
class MSLD extends Car {
@Override
public void run() {
System.out.println("停那里就可以了");
}
public void showBrand() {
System.out.println("鱼叉车牌");
}
}
8多态的应用
多态的两种实现方式
使用父类作为方法形参实现多态
使用父类作为方法返回值实现多态
当这个作为参数的父类是普通类或者抽象类时,构成继承多态
当这个父作为参数的父类是一个接口时,构成接口多态
1多态作为形参
形式参数
基本类型
引用类型
普通类
当一个形参希望我们传入的是一个普通类时,我们实际上传入的是该类的对象/匿名对象
抽象类
当一个形参希望我们传入的是一个抽象类时,我们实际上传入的是该类的子类对象/子类匿名对象
接口
当一个形参希望我们传入的是一个接口时,我们实际上传入的是该类的实现类对象/实现类匿名对象/接口的实现类匿名对象
子类匿名对象的格式[匿名内部类]:
new 普通类/抽象类/接口(){
重写方法;
}
1.本质是一个对象
2.这个对象是普通类/抽象类/接口的子类
3.这个对象没有名字
4.这个对象只能够访问一次
public class ArgsDemo03 {
public static void main(String[] args) {
// 访问StudentDemo中的method方法
IStudent s = new MiddleStudent();
new StudentDemo().method(s);
new StudentDemo().method(new MiddleStudent());
new StudentDemo().method(new IStudent() {
@Override
public void study() {
System.out.println("haha");
}
@Override
public void show() {
System.out.println("hehe");
}
});
}
}
class StudentDemo {
public void method(IStudent s) {
// IStudent s = new MiddleStudent();
/*
* IStudent s = new IStudent() {
@Override
public void study() {
System.out.println("haha");
}
@Override
public void show() {
System.out.println("hehe");
}
}
*
*/
s.study();
s.show();
}
}
interface IStudent {
public abstract void study();
public abstract void show();
}
class MiddleStudent implements IStudent {
@Override
public void study() {
System.out.println("中学生学习");
}
@Override
public void show() {
System.out.println("MiddleStudent.show()");
}
}
2多态作为返回值
返回值类型
基本类型(前面讲过,不解释)
引用类型
普通类
当一个方法的返回值是一个普通的类时,实际上返回的是该类的对象,我们可以使用该类的对象接收
抽象类
当一个方法的返回值是一个抽象类时,实际上返回的是该抽象类的子类对象,我们可以使用该抽象类接收
接口
当一个方法的返回值是一个接口时,实际上返回的是该接口的实现类对象,我们可以使用接口接收
当方法的返回值类型是引用类型的时候,可以使用链式调用
public class ReturnDemo03 {
public static void main(String[] args) {
// IStudent stu = new StudentDemo().method();
// stu.study();
//
// IStudent s = new StudentDemo().method();
// s.study();
IStudent is = new StudentDemo().method(new IStudent() {
@Override
public void study() {
System.out.println("外部 study");
}
});
/*
*
* IStudent is = new IStudent() {
@Override
public void study() {
System.out.println("返回 study");
}
}
*/
is.study();
}
}
interface IStudent {
public abstract void study();
}
class Student implements IStudent {
@Override
public void study() {
System.out.println("Student.study()");
}
}
class StudentDemo {
// public IStudent method() {
return new Student();
// return new IStudent() {
//
// @Override
// public void study() {
// System.out.println("实现类匿名对象");
// }
// };
// }
public IStudent method(IStudent stu) {
/*
* IStudent stu = new IStudent() {
@Override
public void study() {
System.out.println("外部 study");
}
}
*
*/
stu = new IStudent() {
@Override
public void study() {
System.out.println("内部 study");
}
};
return new IStudent() {
@Override
public void study() {
System.out.println("返回 study");
}
};
}
}
16抽象类
1抽象概念:
一个方法没有方法体(没有具体的实现),但是必须存在,这样方法称为抽象方法
2抽象类的特点:
1.抽象方法和抽象类使用 abstract关键字修饰
2.有一个抽象方法的类必须定义为抽象类
3.抽象类中一定有抽象方法吗? 抽象类可以没有抽象方法
4.没有抽象方法的抽象类有意义吗?
a. 防止外界创建对象
b. 成员给子类使用
5.抽象类不能够实例化
6.如果就想实例化怎么做? -- 利用多态
7.抽象类的子类特点
a.如果子类想要继承抽象类,就必须实现父类所有的抽象方法
b.如果子类不想实现父类的抽象方法,那么子类升级为抽象类
8.抽象类和普通类的区别?
抽象类中可以有抽象方法,还可以有成员变量,成员方法,静态方法,构造方法,常量吗? -- 都可以有
有的话有什么意义呢? 都用来给子类使用
成员变量,成员方法 -- 直接送给子类用
常量,静态方法 -- 大家一起用
构造方法 -- 给子类初始化父类继承过来的成员
抽象方法 -- 给子类重写实现多态
归根结底,抽象类的本质是一个服务类,服务于所有子类
9. private final static 可以用来修饰抽象方法吗?
private final和abstract冲突
static 和 abstract 没有意义
10.既然是一个服务类,服务于所有子类,一般抽象类都是用 public 或者 protected 修饰
package com.sxt.abstractdemo;
/*
* 1、编写交通工具类,具有前进run()功能,子类有自行车、小轿车、地铁,重写父类方法,主人有属性name,age属性,
* 方法回家goHome(交通工具),需要使用交通工具,使用抽象类优化程序。
*/
public class AbstractDemo02 {
public static void main(String[] args) {
Hoster h = new Hoster("隔壁老王", 30);
h.goHome(new Bike());
new Hoster("张三", 40).goHome(new Subway());
}
}
class Hoster {
private String name;
private int age;
public Hoster() {
super();
}
public Hoster(String name, int age) {
super();
this.name = name;
this.age = age;
}
// Vehicle v = new Bike();
public void goHome(Vehicle v) {
v.run(name);
}
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;
}
}
abstract class Vehicle {
public abstract void run();
public abstract void run(String name);
}
class Bike extends Vehicle {
@Override
public void run() {
System.out.println("骑着自行车");
}
@Override
public void run(String name) {
System.out.println(name + "骑着自行车回家");
}
}
class Car extends Vehicle {
@Override
public void run() {
System.out.println("开车");
}
@Override
public void run(String name) {
System.out.println(name + "开车回家");
}
}
class Subway extends Vehicle {
@Override
public void run() {
System.out.println("坐地铁回家");
}
@Override
public void run(String name) {
System.out.println(name + "坐地铁回家");
}
}
17接口
1接口的概念:
接口就是常量和抽象方法的集合
接口本质是比抽象类还要抽象的类
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和抽象方法的定义,而没有变量和方法的实现。
2格式
public interface 接口名{
//常量
//抽象方法
}
3接口的定义:
接口的实现类格式:
public 类名 implements 接口{
// 实现接口的方法
//普通方法
接口的实现类的定义
class 接口的实现类 implements 接口名1,接口2,...,接口n {
// 实现每一个接口中的所有的抽象方法
}
4 接口的特点:
1.接口使用 interface 修饰
2.接口是常量和抽象方法的集合, 在JDK1.8以后还可以有静态方法和默认方法
常量:默认可以省略 public static final
抽象方法:默认省略 public abstract 强制重写
静态方法方便调用
默认给子类使用,如果子类不想使用可以重写
3.接口不能实例化
4.如果我想要实例化,可以使用多态 -- 父类引用指向子类实现类对象
5.接口的子类特点:
a.如果一个类实现了一个接口就必须实现接口中所有的抽象方法
b.如果一个类不想要实现接口,那么自己升级为接口
6.接口是一种规范
7.接口是可以扩展功能
8.接口是灵活的,可以多实现,还可以热插拔
10.接口和类之间的关系
类和类 单继承 不可以实现
类和接口 多实现 不可以继承
接口和接口多继承 不可以实现
11.接口和继承的关系
什么时候使用接口,什么时候使用继承
大部分情况都用接口,因为接口灵活,耦合性低,而且还是一种规范,扩展功能 【规范】
继承体现在代码的抽取,或者设计复杂的类 【模板】
12.面向接口编程
package com.sxt.interfacedemo;
/*
* 1、要求如下:
(1) 所有的可以拨号的设备都应该有拨号功能 (Dailup)
(2) 所有的播放设备都可以有播放功能(Play)。
(3) 所有的照相设备都有拍照功能(takePhoto)。
(4) 定义一个电话类 Telephone,有拨号功能。
(5) 定义一个Dvd类有播放功能。
(6) 定义一个照相机类 Camera, 有照相功能。
(7) 定义一个手机类 Mobile, 有拨号,拍照,播放功能。
(8) 定义一个人类 Person, 有如下方法:
<1> 使用拨号设备 use (拨号设备)
<2> 使用拍照设备 use(拍照设备)
<3> 使用播放设备 use(播放设备)
<4> 使用拨号 播放 拍照设备 use(拨号播放拍照设备)
(9) 编写测试类Test 分别创建人,电话,Dvd,照相机,手机对象,让人使用这些对象
ctr + 左键
*/
public class InterfaceDemo03 {
public static void main(String[] args) {
// (9) 编写测试类Test 分别创建人,电话,Dvd,照相机,手机对象,让人使用这些对象
// 利用多态创建人类
AbsPersons p = new Person(); // 继承多态
// 创建各种对象 接口多态
IDailup dailup = new Telephone();
IPlay play = new Dvd();
ITakePhone takePhone = new Camera();
IDailupPlayTakePhone dailupPlayTakePhone = new Mobile();
// 使用设备
p.use(dailup);
System.out.println("======================");
p.use(play);
System.out.println("======================");
p.use(takePhone);
System.out.println("======================");
p.use(dailupPlayTakePhone);
// 总结: 方法重载也是多态的一种表现形式
}
}
// (8) 定义一个人类 Person, 有如下方法
abstract class AbsPersons {
public String name;
public int age;
public AbsPersons() {
super();
}
public AbsPersons(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
public abstract void use(IDailup dailup);
public abstract void use(ITakePhone takePhone);
public abstract void use(IPlay play);
public abstract void use(IDailupPlayTakePhone dailupPlayTakePhone);
}
class Person extends AbsPersons {
/*
* <1> 使用拨号设备 use (拨号设备)
* <2> 使用拍照设备 use(拍照设备)
* <3> 使用播放设备 use(播放设备)
* <4> 使用拨号 播放 拍照设备 use(拨号播放拍照设备)
*/
@Override
public void use(IDailup dailup) {
// IDailup dailup = new Telephone();
dailup.dailup();
}
@Override
public void use(ITakePhone takePhone) {
takePhone.takePhone();
}
@Override
public void use(IPlay play) {
play.play();
}
@Override
public void use(IDailupPlayTakePhone dailupPlayTakePhone) {
// IDailupPlayTakePhone dailupPlayTakePhone = new Mobile();
dailupPlayTakePhone.dailup();
dailupPlayTakePhone.takePhone();
dailupPlayTakePhone.play();
}
}
// (1) 所有的可以拨号的设备都应该有拨号功能 (Dailup)
interface IDailup {
void dailup();
}
// (2) 所有的播放设备都可以有播放功能(Play)
interface IPlay {
void play();
}
// (3) 所有的照相设备都有拍照功能(takePhoto)。
interface ITakePhone {
void takePhone();
}
interface IDailupPlayTakePhone extends IDailup,IPlay,ITakePhone {}
// (4) 定义一个电话类 Telephone,有拨号功能。
class Telephone implements IDailup {
@Override
public void dailup() {
System.out.println("电话拨号!!!");
}
}
// (5) 定义一个Dvd类有播放功能。
class Dvd implements IPlay {
@Override
public void play() {
System.out.println("Dvd播放!!!");
}
}
// (6) 定义一个照相机类 Camera, 有照相功能。
class Camera implements ITakePhone {
@Override
public void takePhone() {
System.out.println("照相机照相!!!");
}
}
// (7) 定义一个手机类 Mobile, 有拨号,拍照,播放功能。
class Mobile implements IDailupPlayTakePhone {
@Override
public void takePhone() {
System.out.println("手机照相");
}
@Override
public void play() {
System.out.println("手机播放");
}
@Override
public void dailup() {
System.out.println("手机拨号");
}
}
18 内部类
1什么情况下外部不能够创建类的对象?
1.抽象类
2.接口
3.构造方法全部私有
4.内部类
2 为什么需要学习内部类?
提高了类的安全性
3 内部类的概念:
将类定义类的内部,那么该类就成为内部类
注意: 内部类是一个相对的概念:
如果A类中有一个B类,那么A类相对于B类来说就是外部类,那么B类相对于A类来说就是内部类
##4 内部类的分类
1.局部内部类
2.成员内部类
3.静态内部类
4.匿名内部类
5 内部类的特点:
1.内部类可以直接访问外部类的私有成员吗? >>>可以
2.外部类可以访问内部类的成员? >>>必须创建内部类的对象
3.如果编译器编译生成字节码文件,那么内部类会不会生成.class文件,如果会的话,格式是?
会生成,格式是 : 外部类类名$内部类类名.class
4.如果在外界要访问内部类对象 可以,可以是可以,后面学习,但是内部类有一个缺陷,会造成类和类 的耦合性过高,慎用
public class InnerClassDemo01 {
public static void main(String[] args) {
}
}
class Outer{
private int num = 10;
// 成员内部类
class Inner{
public int num2 = 20;
public void show() {
System.out.println(num);
}
}
public void test() {
Inner inner = new Inner();
System.out.println(inner.num2);
inner.show();
}
// 静态内部类
static class Inner2{
}
public void show() {
// 局部内部类
class Inner3{
}
// 匿名内部类
new Inter() {
@Override
public void show() {
System.out.println("show");
}
}.show();
new Inter() {
@Override
public void show() {
System.out.println("show2");
}
};
}
}
interface Inter {
void show();
}
6 内部类成员
1格式:
外部类类名.内部类类名 对象 = new 外部类对象.new 内部类对象
2 注意:
一旦内部类成员加上private关键字,外界无法访问
当外部类类名和内部类类名成员重名的时候如何访问?
外部类类名.this.外部类成员
3 如何访问
1.内部类如何访问外部类成员 -- 直接访问
2.外部类如何访问内部类成员 -- 不能直接访问,创建内部类对象
3.外界如果访问内部类成员
外部类类名.内部类类名 对象 = new 外部类对象.new 内部类对象
4.外界如何访问内部类私有成员
无法访问,可以内部类提供对外的公共访问方式
5.当外部类类名和内部类类名成员重名的时候如何访问 -- 外部类类名.this.外部类成员
public class InnerClassDemo02 {
public static void main(String[] args) {
// Outer2 outer2 = new Outer2();
// Outer2.Inner oi = new Outer2().new Inner();
// System.out.println(oi.num);
// System.out.println(outer2.num);
Outer2.Inner oi = new Outer2().new Inner();
oi.show();
}
}
class Fu {
int num = 5;
}
class Outer2 extends Fu{
int num = 10;
// 成员位置
class Inner{
int num = 20;
public void show() {
int num = 30;
System.out.println(num); // 30
System.out.println(this.num); // 20
System.out.println(Outer2.this.num); // 10
System.out.println(Outer2.super.num); // 5
}
}
public void show() {
}
}
7 局部内部类
在局部位置创建内部类对象,访问内部类成员
在JDK1.8之前,如果局部内部类访问外部类成员,需要加final关键字
public class InnerClassDemo04 {
public static void main(String[] args) {
Outer3 o = new Outer3();
o.show();
}
}
class Outer3 {
int a = 20;
public void show() {
int x = 100;
class InnerX {
int num = 10;
public void show() {
System.out.println(num);
System.out.println(a);
}
}
InnerX innerX = new InnerX();
System.out.println(innerX.num);
innerX.show();
}
public void method() {
}
}
8 静态内部类
格式:
外部类类名.内部类类名 对象 = new 外部类对象.内部类类名();
静态内部类中可以有静态成员和非静态成员
访问非静态成员需要创建对象
访问静态成员可以直接调用
外部类类名.内部类类名.静态成员
方便访问静态内部类的成员
public class InnerClassDemo05 {
public static void main(String[] args) {
// 外部类类名.内部类类名 对象 = new 外部类对象.内部类类名();
Outer4.Inner oi = new Outer4.Inner();
System.out.println(oi.num);
System.out.println(oi.num2);
oi.show();
oi.method();
Outer4.Inner.num2 = 200;
Outer4.Inner.method();
}
}
class Outer4 {
static class Inner {
public int num = 10;
public void show() {
System.out.println("Outer4.Inner.show()");
}
public static int num2 = 20;
public static void method() {
System.out.println("Outer4.Inner.method()");
}
}
interface Entry {
}
}
9 匿名内部类
1概念:
本质就是一个子类匿名对象
2特点:
1.是一个子类匿名对象,父类 可以是 普通类 抽象类 接口
2.是一个对象
3.没有名字的子类
前提条件: 存在一个父类(接口 /抽象类/普通类)
3 格式:
没有对象名
没有类名
当时它是一个子类
还是一个对象
new 类名/接口名(){
//重写方法
public class InnerClassDemo06 {
public static void main(String[] args) {
new Student() {
};
new AbsTeacher() {
@Override
void teach() {
// TODO Auto-generated method stub
}
};
new ICar() {
@Override
public void run() {
// TODO Auto-generated method stub
}
};
IDailup dailup = new IDailup() {
@Override
public void show2() {
// TODO Auto-generated method stub
}
@Override
public void show() {
// TODO Auto-generated method stub
}
};
dailup.show();
dailup.show2();
}
}
class CarImpl implements ICar{
@Override
public void run() {
System.out.println("CarImpl.run()");
}
}
interface ICar {
void run();
}
interface IDailup{
void show();
void show2();
}
abstract class AbsTeacher{
abstract void teach();
}
class Student{
public void show() {
System.out.println("Student.show()");
}
}