05-面向对象|封装继承多态
1-2-基本定义-属性-方法
- 面向过程:强调的是功能行为,以函数为最小单位
- 面向对象:强调具备了功能的对象,以类/对象为最小单位
- 属性:成员变量
- 行为:成员方法
//类定义
class Person{
//属性/变量
String name;
int age;
boolean isMale;
//方法
/*
用于定义类应该有的功能
修饰符 返回值类型 方法名(形参){方法体}
权限修饰符:private,default,protected,public
返回值类型: 如果有返回值,则指定类型,如int,String等(return)
如果没有返回值,则写void,方法体中一般就不需要return了,但也可以使用 return; 来结束方法
*/
public void eat(String food){
System.out.println(name + "is eating" + food);
}
}
public class PersonTest{
public static void main(String[] args){
//创建对象
Person p = new Person();
//调用属性
System.out.println(p.age);
//调用方法
p.eat("ningmeng");
}
}
方法重载
- 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
class Test{
//方法重载 形参类型,形参顺序,形参个数不同时构成重载
//如下add方法构成方法重载
public int add(int n1,int n2){
int sum = n1 + n2;
return sum;
}
public int add(int n1,int n2,int n3){
int sum = n1 + n2 + n3;
return sum;
}
public double add(int n1, double n2){
double sum = n1 + n2;
return sum;
}
public double add(double n1,int n2){
double sum = n1 + n2;
return sum;
}
}
形参个数可变的方法
public class Test {
public static void main(String[] args){
Testa test = new Testa();
Testa.add(1,2,3);
}
}
class Testa{
//使用...来表示可变长形参,上面输入的形参1,2,3会被存储在nums数组中 (不能与int[] nums)构成重载
public static void add(int ... nums){ //如果有add(int num1){}方法重载,在传入参数为1个int型时,使用add(int num1){}方法,同理,add(int num1,int num2)...等优先级高于可变形参方法
int sum = 0;
for(int i = 0; i < nums.length; i++){
sum += nums[i];
}
System.out.println(sum);
}
}
递归
class Test{
public int jc(int n){
if(n == 1 || n == 0){
return 1; //递归出口,即递归一定次数后,会从此处结束递归
}else if( n > 1){
return n * jc(n-1); //递归-自己调用自己
}else{
return -1
}
}
}
封装与隐藏
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合:仅对外保留少量的方法用于使用
隐藏对象内部的复杂性,只对外公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。
封装性的体现:
- 将类的属性私有化(
private
),同时提供公共(public
)的方法来获取(getXxx
)和设置(setXxx
)
权限从小到大:private 缺省 protected public
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Y | |||
default(不写) | Y | Y | ||
protected | Y | Y | Y | |
public | Y | Y | Y | Y |
修饰类只能默认或者public
3-构造器
- 构造器:创建对象,初始化对象信息。如果没有定义,则默认为空构造器
class Person{
String name;
int age;
//构造器
public Person(){
//不定义构造器,就默认使用这种方式,如果定义了其他构造器,则默认中无此构造器,需自己手动重载
}
//使用方法重载,可以定义多个构造器,进行对象的初始化
public Person(String name){
this.name = name;
}
public Person(String name,int age){
//this.name = name;
this(name); //可以使用this(形参列表)的方式调用自己的构造器(代码),只能放在第一行
this.age = age;
}
}
包(package)与导入包(import)
- 使用package声明类或接口所属的包,声明在首行(逻辑首行)
- 包属于标识符,遵循标识符的命名规则
- 每"."一次就代表一层目录
//如
package design.cifan.java;
//表示此文件在design目录下的cifan文件夹下的java文件夹中
import design.cifan.java2.Person; //导入java2包中的Person类
//如果有不同包的同名类,则使用时需使用全类名
import design.cifan.java3.Person;
class Test{
design.cifan.java3.Person p = new design.cifan.java3.Person();
}
//import static ... ; 可以导入指定类或接口中的静态结构
import static design.cifan.java4.Person; //这样会报错,不能导入类
import static java.lang.System.out; //导入out(System中out定义为static)
MVC设计模式
- 将整个程序分为三个层次:视图模型层、控制器层、与数据模型层
- 将程序的输入输出、数据处理以及数据的展示分离开
- 模型层:model主要处理数据
- 数据对象封装:
model.bean/domain
- 数据库操作类:
model.dao
- 数据库:
model.db
- 控制层:controller 处理业务逻辑
- 应用界面相关:
controller.activity
- 存放fragment:
controller.fragment
- 显示列表的适配器:
controller.adapter
- 服务相关的:
controller.service
- 抽取的基本类:
controller.base
- 视图层:view 显示数据
- 相关工具类:
view.utils
- 自定义view:
view.ui
用户输入 -> 控制器-----
将指令与数据传递给模型
-------模型(数据库交互)-----根据业务逻辑选择不同的视图
-----视图 -> 反馈给用户
继承性
- 减少代码的冗余,提高代码的复用性
- 便于功能的扩展
- 为多态性的使用提供了前提
public class Student extends Person{ //使用extends继承
//继承之后就有Person的属性与方法了
String major;
public Student(){
}
public Student(String name, int age){
super(name,age); //super相当于父类super()即父类的构造器----与this(形参列表) 只能二选一---如果两个都不写,默认为super()-层层向上调用super()最终会到java.lang.Object类中空参的构造器
//可以super.属性 | super.方法 ---可以调用父类中的属性和方法(特别是已经被重写的方法)
}
public void learn(){
System.out.println("Learing");
}
}
class Person{
String name;
int age;
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("Eating...");
}
}
方法重写
- 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
- 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
eg:父类的方法为public,则子类重写的方法只能是public,不能比它小
—子类不能重写父类的private方法
- 返回值:
-
- 父类被重写的方法的返回值类型是void或基本数据类型,则子类重写的方法返回值需相同
- 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A的子类
- static方法不可以被重写
多态性
- 对象的多态性:父类的引用指向子类的对象
对象的多态性只适用于方法,不适用于属性
class Person{
String name;
int age;
public Person(){
}
//重写eat()
public void eat(){
System.out.println("Eating...");
}
}
class Man extends Person{
public Man(){
}
public void eat(){
System.out.println("Man eating...");
}
}
class Woman extends Person{
public Woman(){
}
//重写eat()
public void eat(){
System.out.println("Woman eating...");
}
}
public class Test{
public static void main(String[] args){
Person p1 = new Man();
Person p2 = new Woman();
//变量声明的时候,声明的是Person类,但赋值赋的是它的子类(自动变量提升)
//此时,可以调用Person类中的方法,但是执行的实际上是子类重写的方法(如果没有重写,则是自身的默认方法)
p1.eat(); //Man eating
p2.eat(); //Woman eating
//多态:即事物的多种形态--当此Person为Man时,执行Man中的eat方法,当此Person为Woman时,执行Woman中的eat方法
//即编译时,看左边(如:如果p1调用Person中没有的方法则会报错);执行时,看右边(即执行时看此对象时Man还是Woman类型
//不能调用Person子类特有的属性和方法,如果要想使用,可以向下转型
Man p3 = (Man)p1; //将p1强制转换为Man类型,然后p3就可以使用Man特有的属性和方法
}
}
应用举例:
class Animal{
public void shout(){
System.out.println("aaa");
}
}
class Dog extends Animal{
public void shout(){
System.out.println("汪汪汪!");
}
}
class Cat extends Animal{
public void shout(){
System.out.println("喵喵喵!");
}
}
public class Test{
public static void main(String[] args){
Test test = new Test();
test.animalShout(new Dog()); //汪汪汪!
test.animalShout(new Cat()); //喵喵喵!
}
public void animalShout(Animal animal){ //如果没有多态性,则此处只能接收Animal类型的参数,需要为Dog类型和Cat类型等重载多个方法才能实现此功能
animal.shout();
}
}
对象的多态性只适用于方法,不适用于属性
instanceof
//a instanceof A 判断对象a是否是A的实例,返回boolean类型---一般用来防止向下转型出现错误
if(p1 instanceof Man){
Man p3 = (Man)p1;
}
if(p1 instanceof Woman){
Woman p4 = (Woman)p1;
}
Object类
java.lang.Object类
-
Object类是所有Java类的根父类
-
如果在类的声明中没有使用extends定义直接父类,则默认为Object类
-
equals()
//equals方法
//"=="符号是比较变量的值(不管类型是否相同,只要值相同就返回true) | 如果是引用类型,则变量的值为地址值
String s1 = "123";
String s2 = "123";
System.out.println(s1 == s2); //false
System.out.println(s1.equals(s2)); //true
//Object类中定义的方法和"=="的作用是相同的,String中对Object类中的equals()方法进行了重写,所以返回的是true(还有Date、File、包装类中都重写了Object类中的equals()方法
//通常情况下,使用equals()都是想你叫两个对象的实体内容是否相同,所以一般都会对自定义类进行equals()方法的重写
//如:
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj instanceof XXX){
XXX x = (XXX)obj;
//比较两个对象的属性是否相同
if(this.a == x.a && this.b = x.b && ...){
return true;
}else{
return false;
}
}else{
return false;
}
}
toString()
//当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
//Object中的toString()
public String toString(){
return getClass().getName + "@" + Integer.toHexString(hashCode());
}
//String、Date、File、包装类等都重写了toString()方法
包装类
- 针对八种基本类型定义相应的引用类型–包装类(封装类)(Byte Short Integer Long Float Double Boolean Character)
//基本数据类型->包装类
int num1 = 10;
Integer num = new Integer(num1);
//自动装箱
Integer num3 = 12; //自动将基本数据类型转化为包装类,然后赋值
//包装类->基本数据类型-调用x.xxValue()
int num2 = num.intValue(); //返回int类型的值
Float f = new Float(12.3);
float f1 = f.floatValue(); //返回float类型的值
//自动拆箱
float f2 = new Float(12.3); //自动将包装类转化成基本数据类型
//由于有自动装箱和自动拆箱,所以与String的转换相同
//包装类|基本数据类型->String
//1. 连接
String str1 = num + "";
//2. 调用String的valueOf()
String str2 = String.valueOf(num);
//包装类的toString()
//String->包装类|基本数据类型
String str = "123";
int num4 = Integer.parseInt(str); //调用包装类的方法,其他包装类分别有Float.parseFloat()、Boolean.parseBoolean...
Object o = true ? new Integer(1) : new Double(2.0) //1.0 --因为三目运算符要求返回的两个数据类型相同,所以将int类型自动提升成了double
static关键字
- 可以用来修饰属性、方法、代码块、内部类
使用static修饰属性–>静态变量:
- 实例变量(非静态变量):创建类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性被修改
- 静态变量:创建类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时结果是修改过的
-
- 静态变量随着类的加载而加载
- 即静态变量的加载要早于对象的创建
- 所以可以不创建对象,直接使用
类.对象
的方式来调用;eg:Math.PI
- 所以其在内存中之家在一次,存在方法去的静态域内。
使用static修饰方法–>静态方法:
-
随着类的加载而加载
-
可以通过
类.方法
来调用 -
静态方法中可以调用静态结构(静态属性…),不能调用非静态结构(因为静态方法早于它们加载)
-
不能使用this,super关键字
单例设计模式
- 采取一种方法,保证整个软件系统中,对某个类只存在一个对象实例
-
- 私有化类的构造器–即不能通过new关键字来创建对象
- 内部创建类的对象-static属性
- 提供公共的方法,返回类的对象-static方法
饿汉式
—对象加载时间过长----线程安全
class Bank{
private Back(){} //1.
private static Bank instance = new Back(); //2.
public static Back getInstance(){ //3.
return instance; //饿汉式---提前造好对象,用时直接返回
}
}
懒汉式
—延迟对象创建,快—(目前的类型)线程不安全(如有好几个线程调用get方法,然后同步创建了对象)
class Bank{
private Bank(){} //1.
private static Bank instance = null; //2.
public static Bank getInstance(){ //3.
if(instance == null){
instance = new Bank(); //懒汉式---需要的时候才会造对象
}
return instance;
}
}
- 应用场景:
-
- 网站计数器:实现同步
- 应用程序的日志应用:只能有一个对象进行操作,否则不好同步
- 数据库连接池
- 读取配置文件的类
- 任务管理器
- 回收站
4-代码块
- {}:用来初始化类或对象
- 两种:加static和不加static—静态代码块|非静态代码块
class Person{
String name;
int age;
public Person(){}
//静态代码块-可用于初始化雷
static{
System.out.println("随着类的加载而加载(且执行)"); //随着类的加载而加载且执行,而且只执行一次
}
//非静态代码块-可用于初始化对象
{
System.out.println("new一个实例对象才加载(且执行)"); //没创建一个对象就会加载且执行一次
}
public void eat(){
System.out.println();
}
}
属性赋值的先后顺序:
- 默认初始化:
int num;
- 显示初始化:
int num = 10;
|| 代码块中赋值{num = 10}
- 构造器中赋值
public Person(){num = 10}
- 通过对象.属性或对象.方法
Person.age = 10
|Person.setAge(10)
final
- 可以用来修饰 类、方法、变量
final class FinalA{ //final修饰表示最终类,表示此类不可被子类继承
}
//class B extends FinalA{} //不能继承,报错
class A[
public final void show(){} //final修饰方法表示此方法不能被重写
]
class B extends A{
//public void show(){} //报错
}
class A{
final int num1 = 10; //修饰变量表示一个常量,不可被修改
final int num2;
{
num1 = 11; //不可修改
num2 = 12; //可以初始赋值。一旦赋值,不可修改
}
}
abstract
- 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。
- 类的设计应该保证父类和子类能够共享特征。
- 有时将一个父类设计得非常抽象,以至于它
没有具体的实例
,这样的类叫做抽象类-abstract修饰
abstract修饰方法-抽象方法
- 不能修饰 私有方法、静态方法、final方法、final的类
/*
abstract修饰类:抽象类
- 不能实例化
- 但有构造器--给子类用
abstract修饰方法:抽象方法
- 没有方法体,只有声明
- 在抽象类中定义
*/
abstract class Person{
public abstract void eat();
}
class Student extends Person{
public void eat(){} //若子类重写了父类中的所有的抽象方法后,子类方可实例化||||否则此子类也是定义为抽象类
}
public static void main(String[] args){
Person p = new Person(){ //此处new的Person可以看作重写了原Person类的抽象方法的一个原Person类的子类,借用其名,也叫Person
@override
public void eat(){
System.out.println("E");
}
} //匿名对象非匿名对象
}
Interface
- 是与类并列的结构
- JDK7以前-
-
- 全局变量:变量都是
public static final
但是书写时可以不写 - 抽象方法:方法都是
public abstract
书写时可以不写
- 全局变量:变量都是
- JDK8:出了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(需要写出default)(
接口中的静态方法只能自己用
) - 接口中不能定义构造器-即接口不能实例化
- Java开发中,接口通过让类去实现(implements)的方式来使用
- 如果类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
- 如果没有,则此实现类仍然要被修饰为抽象类
- Java类可以实现多个接口----->打破了Java类单继承的局限性
class A inplements AA,BB,CC{}
如果子类|实现类 继承的父类和实现的接口中声明了同名同参数
- 在其没有重写此方法的前提下,默认调用的是父类方法
- 实现类中调用接口中的方法:
接口.super.方法();
public class InterfaceTest{
}
interface Flyable{
//全局常量
public static finnal int MAX_SPEED = 7900;
int MIN_SPEED = 1; //接口中,声明为常量--即使不使用上面的修饰符修饰
public abstract void fly();
void stop(); //省略public abstract
}
interface Attackable{
void attack();
}
class Plane implements Flyable{
@Override
public void fly(){ //此处叫实现(也就是重写)
System.out.println("Fly...");
}
@Override
public void stop(){
System.out.println("Stop!");
}
}
class Kite implements Flyable,Attackable{
@Override
public void fly(){ //此处叫实现(也就是重写)
System.out.println("Fly...");
}
@Override
public void stop(){
System.out.println("Stop!");
}
@Override
public void attack(){
System.out.println("Attack!!");
}
}
interface USB{
//一些常量...
//方法
void start();
void stop();
}
class Flash implements USB{
@Override
public void start(){
System.out.println("Flash开始工作!");
}
@Override
public void stop(){
System.out.println("Flash结束工作!");
}
}
class Printer implements USB{
@Override
public void start(){
System.out.println("Printer开始工作!");
}
@Override
public void stop(){
System.out.println("Printer结束工作!");
}
}
class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("传输中...");
usb.stop();
}
}
public class Test{
public static void main(String[] args){
Computer com = new Computer;
//相当于匿名子类等。。。。也有匿名实现类
//创建接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash); //接口也有多态性
//创建接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//创建接口的匿名实现类的非匿名对象
USB phone = new USB(){
@Override
public void start(){
System.out.println("Phone开始工作!");
}
@Override
public void stop(){
System.out.println("Phone结束工作!");
}
}
com.transferData(phone);
//创建接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start(){
System.out.println("MP3开始工作!");
}
@Override
public void stop(){
System.out.println("MP3结束工作!");
}
});
}
}
代理模式
- 安全代理:屏蔽对真实角色的直接访问
- 远程代理:通过代理类处理远程方法调用
- 延迟加载:先加载轻量级的代理对象,真正需要的时候再加载真实对象
分为静态代理和动态代理(JDK自带的动态代理,需要反射等知识)
interface NetWork{
public void browse();
}
class Server implements NetWork{
@Override
public void browse(){
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NewWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work = work;
}
public void check(){
System.out.println("联网之前的检查工作");
}
@Override
public void browse(){
check();
work.browse();
}
}
public class Test(){
public static void main(String[] args){
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse(); //通过调用代理类的方法来调用被代理类的方法
}
}
5-内部类
- Inner class
- 在局部内部类的方法中,如果调用外部类所声明的方法中的局部变量,要求此局部变量声明为final
class Person{
String name;
int age;
//成员内部类
// - 有类的功能---定义属性、方法等等,可以final修饰、abstract修饰等等
// - 作为成员-----可以调用外部类的结构、可以4中权限修饰、可以static修饰
static class Dog{String name;}
class Cat{String name;}
public void eat(){}
//局部内部类
public void method(){
int a = 10;
class Brid{
public void xx(){
System.out.println(a); //JDK8之后默认a为final修饰的
}
Person.this.eat(); //调用外部类的方法
}
}
}
public class Test{
public static void main(Stringp[] args){
//成员内部类
Person.Dog dog = new Person.Dog();
//局部内部类
Person person = new Person();
Person.Bird bird = p.new Bird();
}
}
06-异常处理
-
Error: Java虚拟机无法解决的问题。如:JVM系统内部错误,资源耗尽等
-
Exception: 其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界等
-
- 编译时异常(受检异常)
- IOException
- FileNotFoundException
- ClassNotFoundException
- IOException
- 运行时异常(非受检异常)- RuntimeException
NullPointerException
空指针异常ArrayIndexOutOfBoundsException
数组角标越界、StringIndexOutOfBoundsException
字符串角标越界ClassCastException
NumberFormatException
InputMismatchException
ArithmeticException
- 编译时异常(受检异常)
-
异常处理:
- try-catch-finally:finally中的代码一定会被执行,即使try和catch中由return语句
- throws+异常类型
- 抓抛模型
- “抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出,一旦抛出,在此之后的代码就不再执行
- “抓”:即异常的处理方式–上面两种。
try-catch-finall
try{
//可能发生异常的代码
}catch(异常类型1 变量名1){
//异常处理方式1
}catch(异常类型2 变量名2){
//异常处理方式2
}
...
finally{
//异常处理后执行的代码
}
//像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,我们需要自己手动进行资源的释放。此时的资源释放就需要声明在finally中
public class Test{
public static void main(String[] args){
t(); //返回4-----执行try-发现异常-进入catchNum...--准备return 2----但finally必须执行---进入finally----return3--结束
}
public static int t(){
String str = "123";
str = "abc";
try{
int num = Integer.parseInt(str); //abc转化为int类型时会出现NumFormatException
return 1;
}catch(NumFormatException e){ //如果出现NumFormatException就进入此部分
System.out.println("出现数值转换异常了!");
return 2;
}catch(Exception){
System.out.println("出现异常了!"); //如果异常类型满足字符类关系,子类需要声明在父类上面,否则报错
return 3;
}finally{
System.out.println("finally代码一定会执行!");
return 4;
}
}
}
- 常用异常对象处理方式:
- String getMessage();
- printStackTrace();
throws + 异常类型
class Test{
public static void main(String[] args){
try{
method2(); //由于此时异常已经到达了main()方法,如果再向上抛,就会抛到虚拟机上,然后虚拟机可能就“挂”了,所以要在这里使用try-catch来处理
}catch(IOException e){
e.printStackTrace();
}catch(FileNotFoundExcetion e){
e.printStackTrace();
}
}
public static void method2() throws IOException,FileNotFoundException{ //如果此方法内发生这两个异常,则继续将异常上报给方法的调用者
method1(); //如果method1中抛出异常,则会在此处发生
}
public static void method1() throws FileNotFoundException,IOException{ //如果发生这两个异常,则将异常上报给方法的调用者
File file = new File("hello.txt");
...
}
}
-
try-catch-finally:真正将异常给处理掉了
-
throws:只是将异常给抛给了方法的调用者,并没有处理掉异常
-
重写方法抛出异常:
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常
- 父类没有抛异常,则子类也不能抛异常,此时子类需要自己try-catch-finally
手动抛出异常-throw
- 手动生成一个异常并抛出
class Student{
private int id;
public void regist(int id) throws RuntimeException{
if(id > 0){
this.id = id;
}else{
//手动new一个运行时异常|也可以new一个Exception 字符串参数为message,可以使用getMessage方法获得
throw new RuntimeException("你输入的数据非法!");
}
}
}
public class Test{
public static void main(String[] args){
try{
Student stu = new Student();
stu.regist(-1);
}catch(RuntimeException e){
System.out.println(e.getMessage());
}
}
}
自定义异常类
- 继承现有异常类-一般继承自RuntimeException或Exception
- 提供serialVersionUID–唯一标识序列号
- 提供重载的构造器
public class MyException extends RuntimeException{
static final long serialVersionUID = -1324123432235353646;
public MyException(){}
public MyException(String msg){
super(msg);
}
}
- 关注我的公众号【次烦】,获取更多信息(*^▽^*)