面向对象的基本特征包括:抽象、封装、继承、多态,这三大特征后面细写
抽象(个人理解):将一个具体的事物,根据他的特征和行为,将它抽离成一个不具体的一种概念,一种很大的东西,一种分类,就好比人 根据把它的特征抽离成哺乳动物,一说哺乳动物你并不知道这是一个什么东西,它具体是谁,是哪种动物,你可以通过哺乳动物知道人符合条件,老鼠也符合条件,这里面人和老鼠其实都是哺乳动物的一种具体实例。
没写太清除,看看再描述一遍老师写的笔记:让事物脱离具体化,只将它的类型、属性和行为抽离出来形成一个类,例如针对猫、狗、牛、羊,根据它们的共同特征和共同行为,可以把它们抽象出一个动物类,这个从现实中的物体到程序的类设计就是一个抽象的过程,动物类本身就是泛指,它不指具体的某一种动物,这就是脱离具体化的体现。
构造函数
用途:在实例化的时候调用,常常用来初始化对象,给对象的属性赋初始值
书写规则:访问修饰符 类名([形参]){
//内容
}
类里面其实有一个默认的无参构造函数,当你没写构造函数的时候会默认使用它!但是当你自己写了构造函数时,这个构造函数就会消失。所以当你写了有参构造函数的时候,在实例化时就要传入参数(因为没有无参的构造函数)这是你要是不需要传入参数的话,你可以加上无参的构造函数,这里就涉及到了方法的重载
重载:在同类中不同的方法使用相同的方法名(同类中一种多态性的表现)(同名,不同参:包括顺序不一样,数量不一样,类型不一样,返回值可以不同,访问修饰符也可以不一样)
static(静态的)关键字: 当你使用static修饰方法或者属性时,你可以把该方法或者属性理解为类的专属的方法和属性,它不再是某个具体的对象的方法和属性,它是所有对象所共有的,因为它是"类的属性和方法",既然如此,那你也可以不用实例化对象,直接使用类名来使用static修饰的属性或者方法
注意事项:
static修饰的代码块:
static{
内容
}
写在方法外边的代码块,在类加载时执行该代码块且仅仅执行一次(比构造函数早,构造函数在实例化时才执行)。
用途:常用来做初始化操作
快捷键操作
概念:隐藏内部实现细节,对外提供接口
意义:
封装是如何实现的:
private
或者 protected
)
public
>protected
>friendly
>private
访问范围 | public | protected | private | friendly |
---|---|---|---|---|
同一个类 | √ | √ | √ | √ |
同一包中的其他类 | √ | √ | × | √ |
不同包中的子类 | √ | √ | × | × |
不同包中的非子类 | √ | x | × | × |
概念:继承是类与类之间的关系,被继承的那一方,可以使用基类的方法和属性,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()必须写在第一行,不然不能通过编译
实例化子类对象的执行过程:
父类的静态成员,静态代码块 --> 子类的静态成员,静态代码块 -->父类属性 -->父类构造 -->子类属性 -->子类构造 -->实例化完成
概念:是指子类继承自父类,写一个和父类中方法名和参数相同的方法,子类中的该方法会覆盖父类的方法。(发生在继承当中)
好处,可以子类根据不同的需要来实现不同的行为,父类只用来提供一个模板
注意事项:
继承是重写的大前提,没有继承就没有重写
方法名和参数列表完全一致才叫做重写
子类方法的访问修饰符必须大于或者等于父类方法的访问修饰符
什么是抽象类:使用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中一般产生异常的情况有以下三种:
异常的类型
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的区别 |
---|---|---|
修饰类时,此类不能被继承,修饰方法时,该方法不能被重写,修饰属性时,属性的值不能被更改而且一定要赋值 | java 中的垃圾回收方法,可以清理内存未使用的对象该方法不需要调用,因为java 会自动检测并清理内存,简称为GC |
异常处理的一部分 表示一定执行 除非遇到上面讲的那个语句 |
现学部分的重点是标红部分
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()
;(功能就不解释了,顾名思义,其余两个以此类推)
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
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<e> 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); }
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<student> names = new ArrayList<student> names(); //这时我如果给他存放其他的数据类型就会出错 而且我们在取元素的时候不需要强制类型转换
还有泛型类泛型方法之类的没太多时间去补充,到时候补充和完善。