#面向对象概述
所谓的面向对象是一种编程思想,通过这种思想可以把生活中的复杂事情变得简单化,从原来的执行者变成了指挥者,面向对象是基于面向过程而言的。
我们经常说的面向对象的编程实现(OOP,Object Oriented Programming)
l 面向过程强调的是过程,例如:
1、打开冰箱 2、把大象放进去 3、关上冰箱
l 面向对象强调结果,例如:
1、 饿了,去平台点餐,这个动作就是面向对象。你没有去市场买菜洗菜做饭。。。只要有app就可以了。
2、 衣服脏了,直接甩给女票去处理等着穿干净的就可以了。你没有关注中间的过程。。只要找好对象就可以了。
##面向对象的三大特征
封装性:将相关联的属性封装成一个类
继承性:子类继承父类以及父类的属性
多态性:降低代码之间的耦合度,更利于后期代码的复用性
类:
1、 Java语言最基本单位就是类,类似于类型。
2、 类是一类事物的抽象。
3、 可以理解为模板或者设计图纸。
对象:
每个对象具有三个特点:对象的状态,对象的行为和对象的标识。
1、 对象的状态用来描述对象的基本特征。
2、 对象的行为用来描述对象的功能
3、 对象的标识是指对象在内存中都有一个唯一的地址用来和其他对象区分开来。
4、 类是一类事物的抽象,对象是具体的实现。
类和对象的关系
类抽象化成对象
对象实例化成类
1、 计算机语言是用来描述现实世界事物的。属性+行为
2、 那怎么通过java语言描述呢?通过类来描述事物,把事物的属性当做成员变量,把行为当做成员方法。
分析手机事物:
属性:颜色,尺寸,品牌,价格。。。
行为:打电话,发短信,听音乐。。。。
类:手机类,抽取相同的属性和行为
对象:可以按照模板生产很多个手机,比如1号手机对象,包含特有的成员变量和成员方法
##类和对象的创建与使用
使用new关键字创建对象
package day000000;
public class Test1 {
public static void main(String[] args) {
//p是引用对象,持有了对于Person对象的地址值的引用
//此时的p,含有属性,但都是默认值
Person p = new Person();
//设置属性值
p.name="lisi";
p.age=20;
//调用方法
p.eat();
p.sleep();
}
}
class Person{
//属性--成员变量
String name;
int age;
//行为--方法
void eat(){
System.out.println("吃饭饭");
}
void sleep(){
System.out.println("睡觉觉");
}
}
##对象在内存中的储存
变量指向对象在堆内存中的地址值
当变量被清空这个对象将被垃圾回收机制回收
###多对象的内存结构
当存在多对象时,当前对象1的变量存储着当前对象的地址值指向对象1,当调用xxx函数时会创建对象1
创建对象流程
Person p = new Person();//短短这行代码发生了很多事情
-
把Person.class文件加载进内存
-
在栈内存中,开辟空间,存放变量p
-
在堆内存中,开辟空间,存放Person对象
-
对成员变量进行默认的初始化
-
对成员变量进行显示初始化
-
执行构造方法(如果有构造代码块,就先执行构造代码块再执行构造方法)
-
堆内存完成
-
把堆内存的地址值赋值给变量p ,p就是一个引用变量,引用了Person对象的地址值
匿名对象
没有名字的对象,是对象的简化表示形式。
使用场景:
1、 当被调用的对象只调用一次时(多次会创建多个对象浪费内存)
Demo d = new Demo();
d.sleep();
d.game();
//这个d就是对象的名字。
也可以写成:
new Demo().show();//创建了一个对象调方法
new Demo().game();//又创建了一个对象调方法
构造方法
当创建对象时会主动调用类中的构造方法,构造方法分为无参构造和有参构造
类中会默认存在一个无参构造,当手动创建了一个有参构造方法时这个无惨构造会消失,需要手动创建
构造方法的方法名与类名相同而且无返回值
class User{
public User(){
//这是一个无惨构造
}
}
class Order{
public Order(){
}
public Order(int a){
//这是一个有参构造
}
}
构造方法赋值
class User{
String a;
int b;
public User(String a,int b){
this.a=a;
this.b=b;
}
}
##封装性
封装是指隐藏对象的属性和实现细节,仅仅对外提供公共的访问方式。
好处:
1、 提高安全性
2、 提高重用性
##private关键字
是一个权限修饰符,用于修饰成员变量和成员函数,被私有化的成员只能在本类中访问。
想要修改只能,对外提供公共的,get和set方法。
示列
封装一个学生对象并在内部提供get和set方法为自己创建对象
class Student{
private Student student;
public Student getStudent(){
return new Student();
}
public void setStudent(Student student){
this.student=student;
}
}
##构造代码块和局部代码块
构造代码块
1、 在类的内部,方法外部,的代码块。
2、 通常用于抽取构造方法中的共性代码。
3、 每次调用构造方法前都会调用构造代码块
4、 优先于构造方法加载
class User{
String name;
{//构造代码块优先于构造方法执行
name="json";
}
public User(){
System.out.println(name);
}
}
局部代码块
1、 在方法里面的代码块
2、 通常用于控制变量的作用范围,出了括号就失效
3、 变量的范围越小越好,成员变量会有线程安全问题
4、 总结:执行顺序:
构造代码块是最优先的,局部代码块顺序执行
package day99999;
public class TT {
public static void main(String[] args) {
Student s = new Student();
s.init();
}
}
class Student{
{
System.out.println("构造代码块1");
}
public void init(){
{
System.out.println("局部代码块");
}
}
{
System.out.println("构造代码块2");
}
}
##关键字this
this关键字代表本类当前对象的引用对象
在构造函数中this关键字只能放在第一行
public class User{
String name;
public User(String name){
//当局部变量和成员变量出现重名现象时使用this关键字引用成员变量接收赋值操作
this.name=name;
}
}
###构造方法间的调用
class User{
public User(){
this("调用含参构造");
}
public User(String user){
System.out.println(user);
}
}
##继承extends
继承是面向对象最显著的一个特性。
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类/超类/基类。
这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。
提高复用性:只要继承父类,就能有一样的功能
class A extends c{ //原来的eat()拿走了 }
class B extends c{ //原来的eat()拿走了 }
class c{
public void eat(){
syso("eat");
}
}
特点
1、 使用extends关键字
2、 相当于子类把父类的功能复制了一份
3、 java只支持单继承
4、 继承可以传递(爷爷,儿子,孙子的关系)
5、 不能继承父类的私有成员
6、 继承多用于功能的修改,子类可以拥有父类的功能的同时,进行功能拓展
7、 像是is a 的关系
示列
class Animal{
String name;
public void eat(String name,String eat){
this.name=name;
syso(name+"吃"+eat);
}
}
class Dog extends Animal{
//Dog类继承了Animal吃的属性至于吃什么就需要看当前需要吃什么了
//private修饰的不能被继承
}
##关键字super
1、 通过super关键字可以使用父类的内容
2、 super代表父类的一个引用对象
3、 如果用,必须出现在调用位置的第一行
class Fu{
String name;
}
class Zi{
public Zi(String name){
//super是父类对象的引用对象,调用父类成员属性
super.name=name;
}
}
变量的使用
局部的,成员的,父类的
package day000;
public class Test2_LeiVariable {
public static void main(String[] args) {
Sub2 s= new Sub2();
s.show();
}
}
class Fu2{
int num=20;
}
class Sub2 extends Fu2{
int num;
public void show(){
int num=9;
System.out.println(num);//变量的就近原则
System.out.println(this.num);//调用本类的成员变量,相当于Sub2.num
System.out.println(super.num);//调用父类的成员变量
}
}
方法的使用
继承方法,特有方法,重写方法
package day000;
public class Test3_LeiMethod {
public static void main(String[] args) {
Sub3 s=new Sub3();
s.testShow();//调用普通方法,
s.eat();//调用重写方法,子类已经重写了父类的方法,从此再用,就是儿子的实现方式
}
}
class Fu3{
public void show(){
System.out.println("Fu...show()");
}
public void eat(){
System.out.println("爸爸吃肉");
}
}
class Sub3 extends Fu3{
//普通方法
public void testShow(){
super.eat();//调用父类的eat()
}
//重写方法
//方法签名完全一致的情况就是发生了重写
public void eat(){
System.out.println("儿子吃肉");
}
}
构造方法的使用
1、 子类创建对象时,默认会去访问父类的无参构造方法
2、 在构造方法的第一行,都有一条默认的语句:super();
3、 父类没有无参构造时,可以用super调用父类的其他构造
public class Fu{
public Fu(){
syso("Fu--无参构造");
}
}
public class Zi{
public Zi(){
//创建子类对象时首先执行父类构造方法,当父类没有无参构造时需要自己调用含参构造
super();//superh和this关键字都需要放在构造函数最上面
}
}
###this和super关键字的区别
1、 this代表本类对象的引用,super代表父类对象的引用。
2、 this用于区分局部变量和成员变量
3、 super用于区分本类变量和父类变量
4、 this.成员变量 this.成员方法() this(【参数】)代表调用本类内容
5、 super.成员变量 super.成员方法() super(【参数】),代表调用父类内容
6、 this和super不可以同时出现在同一个构造方法里,他们两个只要出现都得放在第一行,同时出现的话,到底第一行放谁呢。。
重写与重载的区别(Overload和Override的区别)
1、重载:是指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同
2、重写:是存在子父类之间的,子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表和相同的返回类型
3、重写是父类与子类之间多态性的一种表现
4、重载是一类中多态性的一种表现
##静态修饰符static
java中的关键字
用于修饰成员(成员变量和成员方法)
可以修饰静态代码块
特点
1、 可以修饰成员变量,成员方法
2、 随着类的加载而加载,优先于对象加载
3、 只加载一次,就会一直存在,不再开辟新空间
4、 全局唯一,全局共享
5、 可以直接被类名调用
6、 静态只能调用静态,非静态可以随意调用
7、 static不能和this或者super共用,因为有static时可能还没有对象
示列
观察执行顺序
public class Test{
public static void main(String[] args){
User user = new User();
user.speak();
User.a=20;
User.show();//静态属性可以用类名直接调用
}
}
public class User{
public static int a= 10;
public static void show(){
syso(a);
syso("show");
}
public void speak(){
syso("speak");
}
}
静态方法内存图
静态调用关系
静态只能调用静态(变量或者方法),非静态可以访问所有的不管是不是静态
因为当静态属性已经加载的时候非静态属性还没有出来呢,当然无法访问
###静态代码块、构造代码块、局部代码块
概述
1、 静态代码块:在类加载时就加载,并且只被加载一次,一般用于项目的初始化
2、 构造代码块:在调用构造方法前会自动调用,每次创建对象都会被调用
3、 局部代码块:方法里的代码块,方法被调用时才会执行
4、 静态代码块:static{ },位置:在类里方法外
5、 TODO创建测试类,类中写好静态代码块,构造代码块,构造方法,普通方法里嵌套局部代码块。测试他们的执行顺序。
6、 静态 - 构造代码块 - 构造方法 - 局部
class Car{
//静态代码块
//1,在类第一次加载时就加载到内存中并一直驻存,
//并全局共享,直到类消失静态资源才消失
//2,并且静态资源只被加载一次
static {
System.out.println(1);
}
//构造代码块
{
System.out.println(2);
}
// 构造方法
public Car() {
System.out.println(3);
}
public void show() {
// 局部代码块
{
System.out.println(6);
}
System.out.println(4);
System.out.println(5);
}
}
##final关键字
概念
final表示最终的
可以修饰类,方法,成员变量
当一个类不想被子类继承时可以用final修饰自己
特点
final修饰的类不能被继承
final修饰的方法不能被重写
final修饰的变量是个常量
常量的定义方式 static final int AGE = 10;
一般常量定义时和static静态共同使用
final class Fu{//final修饰的类不能被继承
static final int AGE = 10;//final修饰的变量是常量
final void show(){//final修饰的方法不能被重写
}
}
//class Zi extends Fu{//final修饰的类不能继承
//}
##多态
多态指的是同一个实体有多种形式,这是面向兑现设计(OOP)的重要特征
主要指同一对象在不同时刻所表示的对象不一样
好处是可以把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
列如动物分为很多种
示列
class Aniaml{
}
class Dog extends Animal{}
class Sex extends Aniaml{}
特点
多态必须要有继承
多态中父类引用指向子类对象
要有方法的重写
编译看左边运行看右边
示列
class Animal{
void eat(){}//提取共性
}
class Dog extends Animal{
void wan(){
syso("玩");//子类特有的属性
}
void eat(){//子类重写了父类的方法
syso("吃");
}
}
class Test{
public static void main(String[] args){
Animal animal = new Dog();//父类引用指向子类对象
animal.eat();//因为子类重写了父类的方法所以最后调用的是重写之后的属性
animal.wan();//子类特有的属性
}
}
###多态的好处
让我们编程时不用考虑事物具体的属性,就可以使用对象的方法
提高了程序的扩展性和可维护性
###多态的使用
1、成员变量:使用的是父类的
2、成员方法:由于存在重写现象所以使用的是子类的
3、静态成员:随着对象而存在,谁调用的就返回谁的
package cc;
public class K {
public static void main(String[] args) {
Fu2 f = new Zi2();//多态,只能用父类的。。。
//多态的成员变量,就是父类的
System.out.println(f.num);//10
//成员方法,因为存在重写,所以是子类的
f.study();//Zi..study()
//静态成员,谁调用就是谁的
System.out.println(f.count);//10
f.sleep();//Fu..sleep()
}
}
class Fu2{
int num=10;
static int count=10;
public void study(){
System.out.println("Fu..study()");
}
public static void sleep(){
System.out.println("Fu..sleep()");
}
}
class Zi2 extends Fu2{
int num=20;
static int count=20;
public void study(){
System.out.println("Zi..study()");
}
public static void sleep(){
System.out.println("Zi..sleep()");
}
}
##静态变量和实例变量的区别
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
向上转型和向下转型
在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。
在应用中就存在着两种转型方式,分别是:向上转型和向下转型。
比如:父类Parent,子类Child
向上转型:父类的引用指向子类对象Parent p=new Child();
说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类的方法就根据这个引用指向调用子类重写方法。
向下转型(较少):子类的引用的指向子类对象,过程中必须要采取到强制转型。
Parent p = new Child();//向上转型,此时,p是Parent类型
Child c = (Child)p;//此时,把Parent类型的p转成小类型Child
//其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的
说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
##异常
概述
用来封装错误信息的对象。
组成结构:类型,提示,行号。
异常的继承结构
Throwable - 顶级父类
– Error:系统错误,无法修复
– Exception:可修复的错误
–RunTimeException运行时异常
–ClassCastException类型转换异常
–ClassNotFoundException类型未找到
java中对异常的处理方式
向上一级调用的地方抛出
在当前位置进行捕获处理
捕获方式
try{出现异常的代码}catch(异常类型 异常名){处理方式}
抛出方式
public static void main(String[] args)throws Exception{}//throws 关键字后面跟上异常类型
示列
public void static main(String[] args){
try{//java中除数不能为0
int a = new Scanner(System.in).nextInt();
int b = new Scanner(system.in).nextInt();
syso(a/b);
}catch(Exception e){
syso("输入整数错误");
}
}
访问修饰符
抽象类
关键字abstract
java抽象类中可以定义抽象方法,抽象方法就是没有方法体的方法,该抽象方法由其子类进行实现,抽象类可以看作一个只有方法声明没有方法体的特殊类
修饰符 abstract 返回值 方法名(参数列表);
abstract class A{//抽象类
abstract void show();//抽象方法
}
class B extends A{//继承
public void show(){
syso("重写");
}
}
特点
1、 通过java关键字abstract实现
2、 可以修饰方法或者类
3、 抽象类中可以没有抽象方法(由子类去实现)
4、 如果类中有抽象方法,那该类必须定义为一个抽象类
5、 子类继承了抽象类以后,要么还是一个抽象类,要么就把所有抽象方法都重写
6、 多用于多态中
7、 抽象类不可以被实例化
示列
abstract class Animal{
//每个动物都需要吃,所以定义一个抽象方法让子类去重写,实现自己的功能
abstract void eat();
}
抽象类的用法
抽象类页游构造方法,但是抽象类本身不能实例化,构造方法是用来给子类实例化用的
抽象类的变量
抽线类可以有成员变量也可以有常量
抽象类中的成员方法
抽象类中既可以有普通方法也可以有抽象方法
##接口
抽象类和接口的区别
下面比较一下两者的语法区别:
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
4. 抽象类中的抽象方法的访问类型可以是 public,protected 和(默认类型,虽然
eclipse 下不报错,但应该也不行),但接口中的抽象方法只能是 public 类型的,并且默认即为 public abstract 类型。
5. 抽象类中可以包含静态方法,接口中不能包含静态方法
6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是 public static final 类型,并且默认即为 public static final 类型。
interface Animal{
//在接口中成员属性默认是static final修饰的
static final int AGE = 1;
void eat();
}
接口支持多继承和多实现,同时也支持继承的时候多实现
package cn.tedu.interfacedemo;
//这个类用来测试接口的复杂用法:多继承多实现
public class Test4_ComInter {
public static void main(String[] args) {
Interface1 in = new Interface1Impl();
in.save();
in.update();
}
}
//创建接口1
interface Interface1{
void save();
void update();
}
//创建接口2
interface Interface2{
void get();
void delete();
}
//1、打破了java单继承的局限性,因为接口之间可以多继承,多个接口之间逗号隔开
interface Interface3 extends Interface1,Interface2{
void add();
}
//3、接口还可以多实现吗??---可以多实现,只不过接口之间逗号隔开
class ManyImpl implements Interface1,Interface2{
public void save() {}
public void update() {}
public void get() {}
public void delete() {}
}
//4、接口可以继承的同时,多实现?? --
class MoreImple extends ManyImpl implements Interface1,Interface2 {
}
//2、创建实现类,使用3号接口的功能,需要重写几个方法呢??---同时重写1号和2号和3号接口里的所有功能
class Interface3Impl implements Interface3{
@Override
public void save() { }
@Override
public void update() { }
@Override
public void get() { }
@Override
public void delete() { }
@Override
public void add() { }
}
//TODO 创建实现类
class Interface1Impl implements Interface1{
@Override
public void save() {
System.out.println("save()...");
}
@Override
public void update() {
System.out.println("update()...");
}
}
小结
面向对象和面向过程的区别就是面向对象只需要结果至于他实现的过程并不重要,所以java开发者会把一些通用的功能模块进行封装,而框架就是这些通用模块的集合;面向过程需要按照流程去进行实现,代码繁琐,耦合度高不易管理
1、类和类的关系:继承 extends / 单继承 / 单根继承
– 继承的意义:为了提高代码的复用性,减少了代码的编写提高开发效率。
– 方法重写的意义:在不修改父类源码的前提下,在子类中重写业务,从此使用的就是重写后的功能。
– 要求子类的方法声明和父类一样,只要改方法体。
– 有了继承有了重写就产生了多态,多态的意义:为了统一程序的调用标准,标准就是父类。
– 多态 也就是向上转型/向上造型。
– 向下造型的意义:很少用,相当于想要使用子类的特殊功能,还不如直接创建子类对象简单。
– class A extends B
– 其中,A和B都是类,A是子类,B是父类,A就拥有了B的所有功能(除了私有的和构造方法)
– 其他知识点:this 和super ,构造方法,各种代码块…
2、类和接口关系:实现implements / 单实现 / 多实现
– class A implements B,C
– 其中,A是实现类,B和C是接口
– 要求A 可以把 B 和C 接口里的所有 抽象方法 都重写掉,否则 A 就是抽象类
– 接口不能创建对象
– 接口里没有构造方法,接口里都是常量,接口里都是抽象方法
3、接口和接口关系:继承extends / 单继承 / 多继承
– 接口的多继承的关系,打破了java单继承的局限性
– interface A extends B,C
– 其中,A B C 都是接口,A是子接口,同时拥有B和C接口里的所有功能
– class AImpl implements A
– 要求AImpl需要重写A接口里的所有方法(是包含B和C接口的所有方法),否则就是抽象类
4、接口和抽象类的区别!!!
– 相同点:都是抽象层,都不能实例化
– 不同点:
– 1、抽象类用abstract关键字描述,接口用interface
– 2、子类和抽象类之间是extends关系,实现类和接口之间是implements关系
– 3、抽象类中 可以 有构造方法 ,接口里 不能 出现 构造方法
– 4、抽象类里可以有 变量,接口里没有变量全都是静态的常量
– 5、接口里定义常量的语法:public static final String NAME=“jack”,会为变量自动拼接public static final
– 6、抽象类里 可以有普通方法 也可以有 抽象方法,接口都是抽象方法
– 7、抽象类和子类之间是继承关系,而且java中,只支持单继承
– 8、接口突破了java单继承的局限性,因为接口可以多继承也可以多实现,甚至可以继承的同时多实现
– 9、接口的复杂用法
– 多继承: interface A extends B,C 其中A是子接口,同时拥有自己的和BC的功能
– 多实现: class AImpl implements M,N,O,P 其中AImpl是实现类,需要同时重写MNOP的所有抽象方法,否则就是一个抽象类
– 继承的同时多实现: class AImpl extends Object implements M,N 一定是先继承后实现
拓展
abstract注意事项
抽象方法要求子类继承后必须重写。那么,abstract关键字不可以和哪些关键字一起使用呢?以下关键字,在抽象类中。用是可以用的,只是没有意义了。
1、 private:被私有化后,子类无法重写,与abstract相违背。
2、 static:静态的,优先于对象存在。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。
3、 final:被final修饰后,无法重写,与abstract相违背。
接口和抽象类的区
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法,如果要扩展抽象类的新方法,子类将很容易的就能得到这些新方法。
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。