java面向对象编程
面向对象的基本特征
面向对象的基本特征包括:抽象、封装、继承、多态,这三大特征后面细写
抽象(个人理解):将一个具体的事物,根据他的特征和行为,将它抽离成一个不具体的一种概念,一种很大的东西,一种分类,就好比人 根据把它的特征抽离成哺乳动物,一说哺乳动物你并不知道这是一个什么东西,它具体是谁,是哪种动物,你可以通过哺乳动物知道人符合条件,老鼠也符合条件,这里面人和老鼠其实都是哺乳动物的一种具体实例。
没写太清除,看看再描述一遍老师写的笔记:让事物脱离具体化,只将它的类型、属性和行为抽离出来形成一个类,例如针对猫、狗、牛、羊,根据它们的共同特征和共同行为,可以把它们抽象出一个动物类,这个从现实中的物体到程序的类设计就是一个抽象的过程,动物类本身就是泛指,它不指具体的某一种动物,这就是脱离具体化的体现。
构造函数
用途:在实例化的时候调用,常常用来初始化对象,给对象的属性赋初始值
书写规则:访问修饰符 类名([形参]){
//内容
}
类里面其实有一个默认的无参构造函数,当你没写构造函数的时候会默认使用它!但是当你自己写了构造函数时,这个构造函数就会消失。所以当你写了有参构造函数的时候,在实例化时就要传入参数(因为没有无参的构造函数)这是你要是不需要传入参数的话,你可以加上无参的构造函数,这里就涉及到了方法的重载
重载:在同类中不同的方法使用相同的方法名(同类中一种多态性的表现)(同名,不同参:包括顺序不一样,数量不一样,类型不一样,返回值可以不同,访问修饰符也可以不一样)-
static(静态的)关键字: 当你使用static修饰方法或者属性时,你可以把该方法或者属性理解为类的专属的方法和属性,它不再是某个具体的对象的方法和属性,它是所有对象所共有的,因为它是"类的属性和方法",既然如此,那你也可以不用实例化对象,直接使用类名来使用static修饰的属性或者方法
注意事项:
- 静态变量只能写在方法外面,不能写在方法体中
- 静态方法中不能使用this调用其他的非静态方法或者非静态属性。因为就像我刚才所说,静态方法的是属于类中的,而非对象,不用实例化就可以使用,但是this指向当前对象。。。。。不知道怎么说,反正就是this指向的问题。
-
static修饰的代码块:
static{
内容
}写在方法外边的代码块,在类加载时执行该代码块且仅仅执行一次(比构造函数早,构造函数在实例化时才执行)。
用途:常用来做初始化操作
-
快捷键操作
- Eclipse中的生成构造函数的快捷键:Shift+Alt+S键+O键
- Idea中的快捷键:Alt+insert
封装
概念:隐藏内部实现细节,对外提供接口
-
意义:
- 避免非法数据(可以对setter方法进行流程控制)使数据更加安全。
- 增加代码的重用性
-
封装是如何实现的:
- 改变修饰符,所以修饰符的种类有哪些呢(将public改为
private
或者protected
)
- public(公有的)
- private(私有的)
- protected(受保护的)
- friendly(默认)
封装的原理就涉及到了一个访问权限的问题
访问权限:public
>protected
>friendly
>private
访问范围 public protected private friendly 同一个类 √ √ √ √ 同一包中的其他类 √ √ × √ 不同包中的子类 √ √ × × 不同包中的非子类 √ x × × - 设置get/set方法(根据需求 对它加入条件判读)(这个get&set方法就是对外提供的接口)
- 改变修饰符,所以修饰符的种类有哪些呢(将public改为
继承
一、继承
概念:继承是类与类之间的关系,被继承的那一方,可以使用基类的方法和属性,Java中只能单继承(即一个子类只能继承自一个父类)继承要满足 is-a的关系,例如:熊猫是一只哺乳动物;那么熊猫不仅有哺乳动物的特性还有一些特有的属性和行为;假如有多个类,它们有很多类似的地方,那我们就可以将这些共性抽离成一个父类,然后再让他们继承这个父类;
语法,如下面代码所示,在需要继承的类后面加
extends
关键字再在关键字后面加入需要继承的类名
package cn.Sukura.www;
class Animals{
public String name;
public Animals(String name) {
this.name = name;
}
public void sayHi() {
System.out.printf("My name is %s\n",this.name);
}
}
class Pandas extends Animals{
public Pandas(String name) {
//super()必须写在第一行
super(name);
}
}
public class Test {
public static void main(String[] args) {
Pandas p = new Pandas("荣荣");
p.sayHi();
}
}
- 哪些不能被继承
- 被
final
(最终的)修饰的类 - 不能继承父类的
构造方法
- 不能继承被
private
修饰的方法或属性 - 不能继承在不同包下的父类中使用默认修饰符
friendly
的修饰的属性和方法(上面那条和这条其实就是设计一个访问权限的问题)
-
super关键字
super()
调用父类的构造方法,其实和this
的使用是一样的,只不过super是调用关于父类的一切,super.Method()
调用父类方法,super.attribute
调用父类属性,在子类构造中调用super()时,super()必须写在第一行,不然不能通过编译 -
实例化子类对象的执行过程:
父类的静态成员,静态代码块 --> 子类的静态成员,静态代码块 -->父类属性 -->父类构造 -->子类属性 -->子类构造 -->实例化完成
二、重写
概念:是指子类继承自父类,写一个和父类中方法名和参数相同的方法,子类中的该方法会覆盖父类的方法。(发生在继承当中)
好处,可以子类根据不同的需要来实现不同的行为,父类只用来提供一个模板
-
注意事项:
继承是重写的大前提,没有继承就没有重写
方法名和参数列表完全一致才叫做重写
子类方法的访问修饰符必须大于或者等于父类方法的访问修饰符
- 子类方法的返回值必须与父类的返回值相关(返回基本数据类型时子类必须与父类完全一样,返回引用数据类型时,子类的返回值类型与父类的返回值类型相同,或是父类的返回值类型的子类)
- 注解:以@开头的称为注解,@Override是重写的注解,表示该方法是重写方法,如果该方法不满足重写规则,使用该注解时就会报错。
三、抽象类
-
什么是抽象类:使用abstract修饰类是抽象类,语法如下:
public abstract class 类名{ public void sayHi(){ //这是一个正经的方法 } public abstract void sayLove();//这是一个抽象方法 }
-
注意事项:
- 抽象类不能被实例化,它主要的目的就是为了给子类做模板的!
-
abstract
关键字不能和final
或static
一起出现(他们有冲突) - 用
abstract
修饰的方法是抽象方法,它没有方法体,抽象方法必须被子类实现,除非子类也是一个抽象类 - 抽象方法必须写在抽象类中,但抽象类不止有抽象方法
-
abstract
关键字 和final
关键字的异同-
abstract
可以修饰方法和类 **final
可以修饰类,方法,属性**-
abstract
修饰类时 该类不能被实例化,但是可以被子类继承(设计出来就是用来继承的) -
final
修饰类时 该类为最终类,不能被继承 -
abstract
修饰方法时 该方法必须被子类实现除非子类也是一个抽象类,该方法不能有方法体! -
final
修饰方法时为最终方法,不能被重写 -
final
修饰属性时,表示常量,必须赋初始值,并且一旦赋值就不能在修改
-
-
多态
接口
接口的概念:Java中的
接口
是一个全部由抽象方法组成的集合,使用interface
关键字来声明一个接口,接口里面只能有抽象方法和常量,实际上,它就是提供了一种规范,表示一种能力,增强了对于事物的扩展-
对于面向接口编程的理解: 要尽可能的使代码更加简介,复用性更高,我们就可以考虑,我们需要的是什么,我们需要知道它是什么吗?还是只需要知道它能做什么。可能我没说明白,还是写一段代码看看吧
//首先根据业务需要 老师要教小朋友说普通话 class Teacher{ public void teachToTalk(){ //方法 } } class Kids{ String name; public void listenForClass{ //假装里面有代码 } public void readBookLoudly{ //假装里面有代码 } } //这时业务很简单,很容易就实现了
//后来业务升级 老师的服务对象不仅仅只是小孩了 还有其他类型的学生 这时就会比较繁琐了,这时我们可以把他们的共性抽离出来写一个抽象类 也可以实现 class abstract Person{ String name; public abstract void listenForClass(); public abstract void readBookLoudly(); } class Kids extends Person{ public void listenForClass{ //假装里面有代码 } public void readBookLoudly{ //假装里面有代码 } } class Adults extends Person{ public void listenForClass{ //假装里面有代码 } public void readBookLoudly{ //假装里面有代码 } } class Teacher{ public void teachToTalk(){ //方法 } }
//现在你会发现只要服务对象是人都可以解决 业务继续升级 这时老师又需要服务动物 但是动物的技能和前面的不匹配了 而且也不可能实现多种动物,这时你会想到你关注的到底是什么,是他们是谁?还是他们能做什么? public interface ListenAndRead{ public void listen(); public void recite(); } abstract class Person implements ListenAndRead{ } class Child extends Person{//儿童 public void listen(){}//听课 public void recite(){}//朗读 } class Student extends Person{ public void listen(){}//听课 public void recite(){}//朗读 } class Foreigner extends Person{ public void listen(){} public void recite(){} } class Parrot implements ListenAndRead{//鹦鹉 public void listen(){} public void recite(){} } class Teacher{ public void teach(ListenAndReciteAble lar){} }
引用: 要让一个依赖方达到最大的兼容性或扩展性,就要让依赖的对象类型尽量宽泛,当然(为什么不用Object)Object是最宽泛的,但是这就不明确了,而且不能保证依赖方的需要。这是就要跳出一些定式,我需要什么的是什么?还是我需要的是他们会什么?这就变成了面向接口编程。
-
接口的语法
定义接口:
访问修饰符 interface关键字 接口的名字{ } 例如: public interface Call{ void CallByWechat(); }
实现接口:
- 父类和子类的关系叫做继承
- 接口与实现类的关系叫做实现
- 使用
implements
关键字来实现接口,可以实现很多接口(实现多继承)
class Phone implements Call,Play{
public void CallByWechat(){
//实现
}
}
-
接口的特点
不能被实例化
接口中的方法都是抽象方法访问修饰符默认为public 且必须是public
接口可以有属性,但他们只能是公有的静态常量(public static final)写于不写都如此
实现类必须重写父接口的所有方法
接口可以继承接口
接口与抽象类的异同点
接口
抽象类
不能被实例化
可以包含抽象方法
可以包含静态常量
使用interface关键字声明
使用abstract修饰的类
没有构造方法
有构造方法
不能包含非抽象方法,且所有方法必须是public的,只能包含public的静态常量。
可以包含非抽象方法并且可以是非public的,可以包含非public的普通变量。
满足 has-a的关系 表示某有某一种能力 表示什么可以做什么。
满足 is-a的关系 比如哈士奇是一条狗 这里就是继承关系
异常
概念: 程序在运行中出现预料之外的错误
-
在Java中一般产生异常的情况有以下三种:
- Java内部发生错误产生异常,Java虚拟机产生的异常
- 编写代码错误产生的异常,如空指针异常,数组越界等异常,这种异常称为未检查异常
- 通过throw语句手动抛出的异常
-
异常的类型
graph TD A(Throwable)-->B1(Error错误) A -->B2(Exception异常) B2 -->C1(运行时异常即非检查异常) B2 -->C2(非运行时异常即检查异常)
运行时异常:都是RuntimeException 类
及其子类
异常,例如空指针异常、数组越界异常等,这些异常是不检查异常,程序可以选择捕获处理,或者不处理,这些异常一般都是程序的逻辑引起的
检查异常:指运行时异常以外的类型,在类型上都属于Exception及其子类,该类异常必须处理,如果不处理就无法编译通过,常见的有IOException
、ClassNotFoundException
等还有用户自己抛出的异常,如果你不处理的话肯定是会报红的对不对
-
常见的异常
异常类型 说明 Exception
异常类,它是所有异常类的顶级父类。 RuntimeException
运行时异常 ArithmeticException
除0异常,除数不能为0 ArrayIndexOutOfBoundsException
数组下标越界 NullPointerExceptio
空指针异常 ClassNotFoundException
不能加载所需要的类 ClassCastException
对象强制类型转换错误 -
处理的方式,如下代码演示
捕获处理方式
try{ //可能出现异常的代码块 }catch(异常类型 对象名字){ //出现异常时处理方式 }catch(异常类型 对象名字){ //出现异常时处理方式 }finally{ //最后执行这个 无论如何都会执行 除非System.exit() }
捕获异常代码一般根据异常类型从小到大捕获 因为程序出现异常时只会进入第一个符合条件的代码块中
还可以通过抛出的方式处理异常
//将异常往外抛出,不再用本类中的try-catch来处理,让调用该方法的那一方来处理 public class Test { //throws 往外抛出异常 throw 人为制造一个异常 public void playing() throws Exception {//在方法名的小括号后面、大括号前面加上 throws 异常类名1,异常类名2 throw new Exception("作业没写完,还想玩?"); } public static void main(String[] args) { Test t = new Test(); try { t.playing(); } catch (Exception e) { e.printStackTrace(); } } }
- finally、finalize、final的区别
| finally | finalize |final的区别 |
|:-----:|:----:|:----:|
|修饰类时,此类不能被继承,修饰方法时,该方法不能被重写,修饰属性时,属性的值不能被更改而且一定要赋值 |java
中的垃圾回收方法,可以清理内存未使用的对象该方法不需要调用,因为java
会自动检测并清理内存,简称为GC
| 异常处理的一部分 表示一定执行 除非遇到上面讲的那个语句 |
- finally、finalize、final的区别
集合框架
- 为什么要使用集合框架?
数组的弊端:类型必须相同、长度是固定的,如果一组数据在程序运行时并不能确定长度,那就不能再使用数组了
相对于数组,集合框架不限类型,并且长度可变,运用更加灵活。
现学部分的重点是标红部分
Collection
和Map
是java.util
包下的两个集合上级接口
-
Collection:存储单个对象的集合 特点:无序(有序、无序是指在进行插入操作时,插入位置的顺序性
先插的位置在前,后插的位置在后,则为有序,反之无序),不唯一如图所示,它拥有三个子类型集合
List
、Set
和Queue
(重点是List)-
List :有序,不唯一
常用的实现类:
1. `ArrayList` :以`数组`的方式存储,**查询的效率高,插入和删除的效率低** 2. `LinkedList`:以`链表`的方式,**插入和删除的效率高,查询的效率低**,和`ArrayList`相反
List常用方法 功能 add(obj) /add(int index,obj o) 添加对象/在指定位置插入 remove(obj (这是Collection提供的方法))/remove(int index)(这是List提供的方法) 按对象删除/按下标删除 get(int index) 获取对象 size() 获取集合的长度 contains() 查看集合中是否包含某对象 前三个方法
LinkedList
有扩展的方法如addFirst()
,addLast()
;(功能就不解释了,顾名思义,其余两个以此类推)- Set:无序、唯一,典型的实现类有:
HashSet
:以哈希表的形式存储
TreeSet
:以树形表的形式存储
-
Map:存储键(key,value)值对的集合 键唯一,值不唯一。
Map常用方法 | 功能 |
---|---|
put(key,value) | 往Map中添加键值对 |
remove(key) | 移除一对键值对 |
get(key) | 根据键获取值 |
size() | 获取集合的长度 |
values() | 获取值集,返回Collection对象,因为Collection是不唯一的,所以值不唯一 |
keySet() | 获取键集,返回Set对象,因为Set是唯一的,所以键唯一 |
containsKey(key) | 判断集合中是否有某键 |
Map典型的实现类
- HashMap: 允许插入空值,效率高,线程不安全
- Hashtable :不允许插入空值,效率低,但线程安全
- TreeMap
集合的遍历
- java中的foreach
for(类名 对象名:集合){
}
//例如
class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
List l = new ArrayList();
l.add(new Student("Yuan",19));
l.add(new Student("He",19));
for(Object s:l) {
Student stu = (Student)s;
System.out.println(stu.name);
}
}
}
-
使用iterator迭代器
-
如何获得iterator迭代器?
通过Collection提供的方法Iterator
Iterator()方法来得到一个迭代器 Iterator迭代器有哪些方法
方法 功能 boolean hasNext() 判断是否还有下一个元素,如果有返回true,没有返回false E next() 获得下一个元素 -
List list = new ArrayList() {
{
this.add(new Student("张三",20));
this.add(new Student("李四",20));
}
};
Iterator iter= list.iterator();
while(iter.hasNext()) {
Student s = (Student)iter.next();
String name = s.name;
System.out.println(name);
}
- 使用for循环 + Iterator迭代器
List list = new ArrayList() {
{
this.add(new Student("张三",20));
this.add(new Student("李四",20));
}
};
for(Iterator iter = list.iterator();iter.hasNext();) {
Student s = (Student)iter.next();
String name = s.name;
System.out.println(name);
}
//另外 单纯的用for循环也是可以遍历的 因为List是有序的
泛型
为什么要用泛型:先注意集合的特点:集合长度可变,并且可以放任意的引用数据类型,存储的数据类型也可以不一样假如我有一个这样的需求,我需要获取全班所有人的姓名,但有个别人捣乱,把名字写成电话号码,导致我后面的代码出错,这种时候,就需要使用到泛型了,如果没按照我的规定来存放数据就会报错,等于是将运行时期会发生的异常提前到编译时期了。所以可见泛型是一种安全机制,一种规范,它和接口一样在制定规则。
如何理解泛型?大家上学的时候都写过作文吧,考试的时候作文上面的要求经常是题材不限,但是一旦你确认题材了,你就不能再写其他题材的作文了,泛型也是如此,本质上泛型是一个变量,它是需要我们赋值的。
//例如
List names = new ArrayList names();
//这时我如果给他存放其他的数据类型就会出错 而且我们在取元素的时候不需要强制类型转换
还有泛型类泛型方法之类的没太多时间去补充,到时候补充和完善。