相关PDF版资料已同步微信公众号
乐享Coding
欢迎你的关注,二维码在文章底部,获取最全Java学习资料和相关电子书!
面向对象是相对于面向过程而言的。面向过程,强调的 是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对 象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
已经说烂的例子【大象装冰箱】对比面向过程与面向对象。
封装 (Encapsulation)
继承 (Inheritance)
多态 (Polymorphism)
【对象】程序是由对象组成,每个对象包含方法
和属性
。对象来自库和自定义类,关键字new创建对象,是实际存在的该类事物的每个个体,因而也称为实例
(instance)。
Student stu = new Student("小乐",18); //通过Student类有参构造创建一个实例
【类】对一类事物的描述,是抽象的、概念上的定义,也是构造对象的模板(对象的方法和属性在类中定义)。
//Student类
public class Student {
//私有属性
private String name;
private int age;
//有参构造器
public Student(String name, int age) {
this.name = name;
//this指向该方法的调用者
this.age = age;
}
//无参构造器
public Student() {
}
//公开get方法,获得对象的属性值
public String getName() {
return name;
}
//公开set方法,设置对象的属性值
public void setName(String name) {
this.name = name;
}
}
【构造器】特殊方法,构造并初始化对象。
构造器至少有一个,不写默认提供一个无参构造器,构造器可以重载多个
【final修饰私有属性】不能修改
private final String name = "小乐"; // final关键字必须初始化赋值
简单小例子:类 = 抽象概念的人;对象 = 实实在在的某个人
指向方法的调用者,常用于类中的方法和有参构造器,目的是区分传来的参数和实例的属性(本例中两个都命名为了name),
this在访问本类中的属性和方法时,如果本类没有此属性和方法则从父类中继续查找。
【方法重载overload】:
概念
特点
//重载举例
public void println(byte x)
public void println(short x)
public void println(int x)
❗❗❗【只有值传递】
将实际参数值的副本 (复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
public static void main(String[] args) {
int x = 15;
System.out.println("修改之前x = " + x);// 15
// x是实参
change(x);
System.out.println("修改之后x = " + x);// 15
}
public static void change(int x) {
System.out.println("change:修改之前x = " + x);
x = 18;
System.out.println("change:修改之后x = " + x);
}
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
public static void main(String[] args) {
Student obj = new Student();
obj.age = 15;
System.out.println("修改之前age = " + obj.age);// 15
// x是实参
change(obj);
System.out.println("修改之后age = " + obj.age);// 18
}
public static void change(Student obj) {
System.out.println("change:修改之前age = " + obj.age);
obj.age = 18;
System.out.println("change:修改之后age = " + obj.age);
}
【包】类的集合,目的是确保类名的唯一性,即同一个类名放在不同包下不会冲突。package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。
package 顶层包名.子包名;
【类的导入】import关键字
import java.util.Arrays; //导入java包下的Arrays类
import java.util.*;//可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
【jar包】将类文件和其他文件打包压缩成一个以.jar为后缀的单个可执行文件,采用zip压缩格式压缩。
java -jar [jar包所在路径下] #执行jar包
为什么需要封装?
【举例】我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内 部的结构吗?有必要碰电动机吗?
【概念】类处理对象的方式,相当于盒子,外部不能直接访问类的属性(属性前加private关键字),只能调用实例的公开的方法(public)。Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作。
【思想】一句话概括
所谓【JavaBean】,是指符合如下标准的Java类
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
大家都知道面向对象继承是三大特性之一,为何需要
继承
呢?
【举例】公司既有员工(Employee类)也有经理(Manager类),他们都领取薪水,但是经理在完成业绩之后还能得到奖金,这种情况下,得奖金是新功能,而领薪水是二者都具有的功能(方法和属性),经理要再写一遍代码就会变得冗余。因此继承的就是为了减少代码冗余,继承并扩展父类的方法和属性。
父类Employee:
//父类属性
public class Employee {
String name;
int age;
String gender;
//父类方法
public void getSalary(){
System.out.println(name+"领取了本月工资!");
}
}
TIPS:上述代码省略了父类get和set方法,实际应该有
子类Manager:
public class Manager extends Employee{
//继承父类
private double bonus = 10000.00; //子类扩展属性
public void getBonus(){
System.out.println(name+"获得了"+bonus+"奖金!"); //子类扩展方法,name是继承来的父类属性
}
}
主类Main(用于测试):
public class Main {
public static void main(String[] args) {
Manager manager = new Manager(); //创建子类对象
manager.setName("韩总"); //初始化从父类继承来的属性name
manager.setGender("男");//初始化从父类继承来的属性gender
manager.getSalary(); //调用父类方法
manager.getBonus(); //调用子类扩展方法
}
}
运行结果
关键字 extends(用在子类),子类继承父类的方法和属性并且可以扩展新的方法和属性。Java仅支持一个父类一个或多个子类继承。
子类不能直接访问父类中私有的(private)的属性和方法。
扩展:
如果父类加了final修饰类,那么此类不会被其他类继承。
//父类 public final class Employee
final修饰方法,子类就不能覆盖(重写)这个方法。
子类重写父类的方法之后会优先调用子类的方法。
【概念】在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器
public String getInfo() {
return super.getInfo();
}
是所有Java类的根父类,如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类。
常见方法:
equals方法
public boolean equals(Object obj)
判断两个对象是否相等
默认object的equals方法
//源码中仅仅是判断两个对象的引用地址是否相同,不能比较属性字段是否相等,一般需要重写。
public boolean equals(Object obj) {
return (this == obj);
}
类中重写后的equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
if (age != employee.age) return false;
if (name != null ? !name.equals(employee.name) : employee.name != null) return false;
return gender != null ? gender.equals(employee.gender) : employee.gender == null;
}
hashCode方法
public int hashCode()
根据对象内存地址返回一个整型值,没有规律,不同对象返回值基本不相同。
Object obj =new Object();
System.out.println(obj.hashCode()); //356573597
toString方法
public String toString()
返回对象值的一个字符串,返回类名和它的引用地址,主要用于调试时能够清晰的看到对象当前状态。一般需要重写
!
测试代码
Employee employee = new Employee(); //创建子类对象
employee.setName("韩总"); //初始化从父类继承来的属性name
employee.setGender("男");//初始化从父类继承来的属性gender
System.out.println(employee.toString());
不重写的情况下
//com.Extend.Employee@4677a629
重写的情况下
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
//Employee{name='韩总', age=0, gender='男'}
getClass方法
返回一个Class类的对象
Class cl = employee.getClass();
装箱:包装类使得一个基本数据类型的数据变成了类。
拆箱:一个包装类变成了一个基本数据类型。
int i = 500;
Integer t = new Integer(i); //装箱
int j = t.intValue(); // j = 500,intValue取出包装类中的数据
父类的引用指向子类的对象,子类的对象可以替代父类的对象使用,因此一个父类如果有多个子类就会有多种形态。
Employee e = new Manager(); //多态
多态情况下
“看左边” :看的是父类的引用(父类中不具备子类特有的方法)
“看右边” :看的是子类的对象(实际运行的是子类重写父类的方法)
引用类型变量声明为父类的类型,但实际引用的是子类对象,左父右子,左接口右实现
属性是在编译时确定的,方法的调用是在运行时确定,编译时,看左边;运行时,看右边。
我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的属性。
在Java类中,可用static修饰属性、方法、代码块、内部类
age是一个实例变量(instance variable),它属于类的每一个对象,不能被同一个类的不同对象所共享。
private int age;
如果想让一个类的所有实例共享数据,就用类变量!
private static int age; //此属性不能用于有参构造器
【static修饰私有属性】属性属于本类,创建100个实例时也只会1个公共的静态属性。
随着类的加载而加载
优先于对象存在
修饰的成员,被所有对象所共享
访问权限允许时,可不创建对象,直接被类调用
没有对象的实例时,可以用类名.方法名()的形式访问由static修饰的类方法。
//没有创建对象也可以访问静态方法
System.out.println("Number of total is " + Person.getTotalPerson());
在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
public class Person {
private int id; //私有属性
private static int total = 0; //静态私有属性
public static void setTotalPerson(int total) {
//this.total = total; //非法,因为不需要实例就可以访问static方法,在static方法中不能有this,也不能有super
}
public static int getTotalPerson() {
//id++; //非法 只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
return total;
}
public Person() {
total++;
id = total;
}
}
对Java类或对象进行初始化
一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块
(static block)
public static int total;
static {
total = 100;
System.out.println("in static block!");
}
没有使用static修饰的,为非静态代码块
。
private int id; //私有属性
private static int total = 0; //静态私有属性
{
total = 100;
id =10;
System.out.println("in block!");
}
常量名尽量要大写,内容不可修改
private final int ID =0; //私有属性 【类中必须进行初始化赋值】
static final:全局常量
private static final int ID =0; //私有静态属性 【类中必须进行初始化赋值】
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一 般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
简单来说就是抽象类会声明空的抽象方法去要求子类必须重写这个方法,目的是确保子类含有这个方法。
用abstract关键字来修饰一个类,这个类叫做抽象类。
public abstract class abstractClass {
抽象方法
}
用abstract来修饰一个方法,该方法叫做抽象方法。
public abstract void talk(); //子类必须重写
抽象类不能实例化(new)
子类必须重写所有父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,则子类仍为抽象类。
//子类重写抽象方法
@Override
public void talk() {
System.out.println("我是用嘴说话的!");
}
不能用abstract修饰变量、代码块、构造器;
不能用abstract修饰私有方法、静态方法、final的方法、final的类。
为什么需要抽象类?
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模 板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象 类的行为方式。
在软件开发中实现一个算法时,整体步骤很固定、通用, 这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
接口不是类,而是对希望符合这个接口的类的一组需求,一个类可以实现多个接口,实现【多重继承
】。
用interface来定义。
public interface Runner {
} //定义一个接口
接口中的所有成员变量都默认是由public static final修饰的。
public static final int ID = 1; //默认可以不加public static final关键字
接口中的所有抽象方法都默认是由public abstract修饰的。
public abstract void start();
接口中没有构造器。
接口也可以继承其它接口,并且可以多继承。
public interface interfaceeB extends interfaceA,interfaceC {
int id = 0;
}
接口和继承可以同时存在
class SubClass extends SuperClass implements InterfaceA{
}
接口和抽象类的作用相似,但是相比抽象类就要更加灵活,子类只能继承一个抽象类,但接口不需要子类继承,而是引入了一个新的关键字implements去实现,且一个类可以实现多个接口。
interface Filial {
void help();
}
interface Spoony {
void talk();
}
static class Man implements Filial, Spoony {
@Override
public void help() {
System.out.println("我该怎么办呢?");
}
@Override
public void talk() {
System.out.println("我想说!");
}
}
Java8,接口中也可以定义具体的方法,需要在方法前加上default关键字。
public interface interfaceC {
default void fun1(){
//C接口定义默认方法
System.out.println("我是C接口的默认方法fun1");
}
}
public class Person implements interfaceC {
} //Person类实现C接口
Person person = new Person();
person.fun1(); //调用接口默认方法
//我是C接口的默认方法fun1
如果Person类实现多个接口有着同样的fun1()方法,为解决冲突,实现类重写默认方法,可以这样调用。
@Override
public void fun1() {
interfaceC.super.fun1();//指明调用interfaceC默认方法
}
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。(类中包含类)
【概念】
外部类方法中定义一个内部的类
package com.Extend;
class Outer {
private int s = 1000;
public void ma() {
class inner{
private String name;
public void mb(){
System.out.println("我是局部内部类!");
}
}
new inner().mb();
}
//new inner().mb(); 不能在方法外创建inner对象
}
public class InnerTest {
public static void main(String args[]) {
Outer o = new Outer();
o.ma();
}
}
package com.Extend;
class Outer {
private int s = 1000;
protected class Inner {
//内部类定义属性
private String name;
//内部类有参构造
public Inner(String name) {
this.name = name;
}
//内部类方法
public void mb() {
System.out.println("在内部类Inner中s=" + s);//可以使用外部变量
}
}
public void ma() {
Inner i = new Inner("小乐");//创建内部类对象
i.mb(); //调用内部类方法
}
}
public class InnerTest {
public static void main(String args[]) {
Outer o = new Outer();
o.ma();
}
}
作为类的成员的角色
作为类的角色
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一 个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或 实现一个类。
public class Outer {
interface A {
void fun1();
}
public static void main(String[] args) {
new Outer().callInner(new A() {
//实现接口
//接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名
public void fun1() {
System.out.println("implement for fun1");
}
});// 两步写成一步了
}
public void callInner(A a) {
a.fun1();
}
}
特点
匿名内部类必须继承父类或实现接口
匿名内部类只能有一个对象
匿名内部类对象只能使用多态形式引用
判断左边的对象是否是右边类或者该类的子类创建的实例对象,是返回true,不是返回false。
Manager manager = new Manager(); //创建子类对象
boolean b = manager instanceof Object; //true
检查一个对象是否实现了某个特定接口,是返回true,不是返回false。
Person person = new Person(); //创建对象
System.out.println(person instanceof Comparable);//判断person类是否实现了Comparable接口
为什么使用异常处理机制?
在编写程序时,经常要在可能出现错误的地方加上检测的代码, 如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据 而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。
在Java中,某个方法出现错误会抛出一个封装了错误信息的对象,此刻方法会立即退出,并不会返回任何值,异常处理机制开始搜索能够处理这种异常情况的异常处理器。
异常对象都是继承与Throwable类的一个实例,如果内置异常类还不满足需求,用户也可自创异常类。
Error类:
Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。
Exception类:
其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。
对于这些错误,一般有两种解决方法:
一是遇到错误就终止程序 的运行。
二是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。 比如:除数为0,数组下标越界等
是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序 员应该积极避免其出现的异常。
是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一 般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}
[ finally{
...... //无论是否发生异常,都无条件执行的语句
} ]
public void readFile(String file) throws FileNotFoundException {
……
// 读文件的操作可能产生FileNotFoundException类型的异常
FileInputStream fis = new FileInputStream(file);
..……
}
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。
IOException e = new IOException();
throw e; //在异常代码下手动抛出异常
需要手动抛出自定义类异常
要继承异常类
调用父类的构造器
class MyException extends Exception {
static final long serialVersionUID = 13465653435L;
private int idnumber;
public MyException(String message, int id) {
super(message); //调用父类的构造器
this.idnumber = id;
}
public int getId() {
return idnumber;
}
}
public class MyExpTest {
public void regist(int num) throws MyException {
if (num < 0)
throw new MyException("人数为负值,不合理", 3);
else
System.out.println("登记人数" + num);
}
public void manager() {
try {
regist(-100);
} catch (MyException e) {
System.out.print("登记失败,出错种类" + e.getId());
}
System.out.print("本次登记操作结束");
}
public static void main(String args[]) {
MyExpTest t = new MyExpTest();
t.manager();
}
}
需要手动抛出自定义类异常
要继承异常类
调用父类的构造器
class MyException extends Exception {
static final long serialVersionUID = 13465653435L;
private int idnumber;
public MyException(String message, int id) {
super(message); //调用父类的构造器
this.idnumber = id;
}
public int getId() {
return idnumber;
}
}
public class MyExpTest {
public void regist(int num) throws MyException {
if (num < 0)
throw new MyException("人数为负值,不合理", 3);
else
System.out.println("登记人数" + num);
}
public void manager() {
try {
regist(-100);
} catch (MyException e) {
System.out.print("登记失败,出错种类" + e.getId());
}
System.out.print("本次登记操作结束");
}
public static void main(String args[]) {
MyExpTest t = new MyExpTest();
t.manager();
}
}