Java面向对象编程(浅)

java面向对象编程

面向对象的基本特征

面向对象的基本特征包括:抽象、封装、继承、多态,这三大特征后面细写
抽象(个人理解):将一个具体的事物,根据他的特征和行为,将它抽离成一个不具体的一种概念,一种很大的东西,一种分类,就好比人 根据把它的特征抽离成哺乳动物,一说哺乳动物你并不知道这是一个什么东西,它具体是谁,是哪种动物,你可以通过哺乳动物知道人符合条件,老鼠也符合条件,这里面人和老鼠其实都是哺乳动物的一种具体实例。
没写太清除,看看再描述一遍老师写的笔记:让事物脱离具体化,只将它的类型、属性和行为抽离出来形成一个类,例如针对猫、狗、牛、羊,根据它们的共同特征和共同行为,可以把它们抽象出一个动物类,这个从现实中的物体到程序的类设计就是一个抽象的过程,动物类本身就是泛指,它不指具体的某一种动物,这就是脱离具体化的体现。

  1. 构造函数
    用途:在实例化的时候调用,常常用来初始化对象,给对象的属性赋初始值
    书写规则:访问修饰符 类名([形参]){
    //内容
    }
    类里面其实有一个默认的无参构造函数,当你没写构造函数的时候会默认使用它!但是当你自己写了构造函数时,这个构造函数就会消失。所以当你写了有参构造函数的时候,在实例化时就要传入参数(因为没有无参的构造函数)这是你要是不需要传入参数的话,你可以加上无参的构造函数,这里就涉及到了方法的重载
    重载:在同类中不同的方法使用相同的方法名(同类中一种多态性的表现)(同名,不同参:包括顺序不一样,数量不一样,类型不一样,返回值可以不同,访问修饰符也可以不一样)

  2. static(静态的)关键字: 当你使用static修饰方法或者属性时,你可以把该方法或者属性理解为类的专属的方法和属性,它不再是某个具体的对象的方法和属性,它是所有对象所共有的,因为它是"类的属性和方法",既然如此,那你也可以不用实例化对象,直接使用类名来使用static修饰的属性或者方法

    注意事项:

    1. 静态变量只能写在方法外面,不能写在方法体中
    2. 静态方法中不能使用this调用其他的非静态方法或者非静态属性。因为就像我刚才所说,静态方法的是属于类中的,而非对象,不用实例化就可以使用,但是this指向当前对象。。。。。不知道怎么说,反正就是this指向的问题。
  3. static修饰的代码块:

    static{
    内容
    }

    写在方法外边的代码块,在类加载时执行该代码块且仅仅执行一次(比构造函数早,构造函数在实例化时才执行)

    用途:常用来做初始化操作

  4. 快捷键操作

    1. Eclipse中的生成构造函数的快捷键:Shift+Alt+S键+O键
    2. Idea中的快捷键:Alt+insert

封装

概念:隐藏内部实现细节,对外提供接口

  1. 意义:

    1. 避免非法数据(可以对setter方法进行流程控制)使数据更加安全。
    2. 增加代码的重用性
  2. 封装是如何实现的:

    1. 改变修饰符,所以修饰符的种类有哪些呢(将public改为private 或者 protected
      • public(公有的)
      • private(私有的)
      • protected(受保护的)
      • friendly(默认)
        封装的原理就涉及到了一个访问权限的问题
        访问权限:public>protected>friendly>private
    访问范围 public protected private friendly
    同一个类
    同一包中的其他类 ×
    不同包中的子类 × ×
    不同包中的非子类 x × ×
    1. 设置get/set方法(根据需求 对它加入条件判读)(这个get&set方法就是对外提供的接口)

继承

一、继承

  1. 概念:继承是类与类之间的关系,被继承的那一方,可以使用基类的方法和属性,Java中只能单继承(即一个子类只能继承自一个父类)继承要满足 is-a的关系,例如:熊猫是一只哺乳动物;那么熊猫不仅有哺乳动物的特性还有一些特有的属性和行为;假如有多个类,它们有很多类似的地方,那我们就可以将这些共性抽离成一个父类,然后再让他们继承这个父类;

  2. 语法,如下面代码所示,在需要继承的类后面加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();
   }
}
  1. 哪些不能被继承
  • final(最终的)修饰的类
  • 不能继承父类的构造方法
  • 不能继承被private修饰的方法或属性
  • 不能继承在不同包下的父类中使用默认修饰符friendly的修饰的属性和方法(上面那条和这条其实就是设计一个访问权限的问题)
  1. super关键字

    super()调用父类的构造方法,其实this的使用是一样的,只不过super是调用关于父类的一切,super.Method()调用父类方法,super.attribute调用父类属性,在子类构造中调用super()时,super()必须写在第一行,不然不能通过编译

  2. 实例化子类对象的执行过程:

    父类的静态成员,静态代码块 --> 子类的静态成员,静态代码块 -->父类属性 -->父类构造 -->子类属性 -->子类构造 -->实例化完成

二、重写

  1. 概念:是指子类继承自父类,写一个和父类中方法名和参数相同的方法,子类中的该方法会覆盖父类的方法。(发生在继承当中)

  2. 好处,可以子类根据不同的需要来实现不同的行为,父类只用来提供一个模板

  3. 注意事项:

    • 继承是重写的大前提,没有继承就没有重写

    • 方法名和参数列表完全一致才叫做重写

    • 子类方法的访问修饰符必须大于或者等于父类方法的访问修饰符

  • 子类方法的返回值必须与父类的返回值相关(返回基本数据类型时子类必须与父类完全一样,返回引用数据类型时,子类的返回值类型与父类的返回值类型相同,或是父类的返回值类型的子类)
  1. 注解:以@开头的称为注解,@Override是重写的注解,表示该方法是重写方法,如果该方法不满足重写规则,使用该注解时就会报错。

三、抽象类

  1. 什么是抽象类:使用abstract修饰类是抽象类,语法如下:

    public abstract class 类名{
        public void sayHi(){
            //这是一个正经的方法
        }
        public abstract void sayLove();//这是一个抽象方法
    }
    
  2. 注意事项:

    • 抽象类不能被实例化,它主要的目的就是为了给子类做模板的!
    • abstract关键字不能和finalstatic一起出现(他们有冲突)
    • abstract修饰的方法是抽象方法,它没有方法体,抽象方法必须被子类实现,除非子类也是一个抽象类
    • 抽象方法必须写在抽象类中,但抽象类不止有抽象方法
  3. abstract关键字 和 final关键字的异同

    • abstract可以修饰方法和类 ** final可以修饰方法属性**
      • abstract修饰类时 该类不能被实例化,但是可以被子类继承(设计出来就是用来继承的)
      • final修饰类时 该类为最终类,不能被继承
      • abstract修饰方法时 该方法必须被子类实现除非子类也是一个抽象类,该方法不能有方法体!
      • final修饰方法时为最终方法,不能被重写
      • final修饰属性时,表示常量,必须赋初始值,并且一旦赋值就不能在修改

多态

接口

  1. 接口的概念:Java中的接口是一个全部由抽象方法组成的集合,使用interface关键字来声明一个接口,接口里面只能有抽象方法和常量,实际上,它就是提供了一种规范,表示一种能力,增强了对于事物的扩展

  2. 对于面向接口编程的理解: 要尽可能的使代码更加简介,复用性更高,我们就可以考虑,我们需要的是什么,我们需要知道它是什么吗?还是只需要知道它能做什么。可能我没说明白,还是写一段代码看看吧

    //首先根据业务需要 老师要教小朋友说普通话 
    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是最宽泛的,但是这就不明确了,而且不能保证依赖方的需要。这是就要跳出一些定式,我需要什么的是什么?还是我需要的是他们会什么?这就变成了面向接口编程。

  3. 接口的语法

    定义接口:

    访问修饰符 interface关键字 接口的名字{
        
    }
    例如:
    public interface Call{
        void CallByWechat();
    }
    

    实现接口:

    • 父类和子类的关系叫做继承
    • 接口与实现类的关系叫做实现
    • 使用implements关键字来实现接口,可以实现很多接口(实现多继承)
    class Phone implements Call,Play{
        public void CallByWechat(){
            //实现
        }
    }
    
  4. 接口的特点

    • 不能被实例化

    • 接口中的方法都是抽象方法访问修饰符默认为public 且必须是public

    • 接口可以有属性,但他们只能是公有的静态常量(public static final)写于不写都如此

    • 实现类必须重写父接口的所有方法

    • 接口可以继承接口

  5. 接口与抽象类的异同点

接口 抽象类
不能被实例化
可以包含抽象方法
可以包含静态常量
使用interface关键字声明 使用abstract修饰的类
没有构造方法 有构造方法
不能包含非抽象方法,且所有方法必须是public的,只能包含public的静态常量。 可以包含非抽象方法并且可以是非public的,可以包含非public的普通变量。
满足 has-a的关系 表示某有某一种能力 表示什么可以做什么。 满足 is-a的关系 比如哈士奇是一条狗 这里就是继承关系

异常

  1. 概念: 程序在运行中出现预料之外的错误

  2. 在Java中一般产生异常的情况有以下三种:

    • Java内部发生错误产生异常,Java虚拟机产生的异常
    • 编写代码错误产生的异常,如空指针异常,数组越界等异常,这种异常称为未检查异常
    • 通过throw语句手动抛出的异常
  3. 异常的类型

    graph TD A(Throwable)-->B1(Error错误) A -->B2(Exception异常) B2 -->C1(运行时异常即非检查异常) B2 -->C2(非运行时异常即检查异常)

运行时异常:都是RuntimeException 类及其子类异常,例如空指针异常、数组越界异常等,这些异常是不检查异常,程序可以选择捕获处理,或者不处理,这些异常一般都是程序的逻辑引起的

检查异常:指运行时异常以外的类型,在类型上都属于Exception及其子类,该类异常必须处理,如果不处理就无法编译通过,常见的有IOExceptionClassNotFoundException 等还有用户自己抛出的异常,如果你不处理的话肯定是会报红的对不对

  1. 常见的异常

    异常类型 说明
    Exception 异常类,它是所有异常类的顶级父类。
    RuntimeException 运行时异常
    ArithmeticException 除0异常,除数不能为0
    ArrayIndexOutOfBoundsException 数组下标越界
    NullPointerExceptio 空指针异常
    ClassNotFoundException 不能加载所需要的类
    ClassCastException 对象强制类型转换错误
  2. 处理的方式,如下代码演示

    捕获处理方式

    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();		}	}}
    
    1. finally、finalize、final的区别
    finally finalize final的区别
    修饰类时,此类不能被继承,修饰方法时,该方法不能被重写,修饰属性时,属性的值不能被更改而且一定要赋值 java中的垃圾回收方法,可以清理内存未使用的对象该方法不需要调用,因为java会自动检测并清理内存,简称为GC 异常处理的一部分 表示一定执行 除非遇到上面讲的那个语句

集合框架

  1. 为什么要使用集合框架?
    数组的弊端:类型必须相同、长度是固定的,如果一组数据在程序运行时并不能确定长度,那就不能再使用数组了
    相对于数组,集合框架不限类型,并且长度可变,运用更加灵活。

现学部分的重点是标红部分

CollectionMapjava.util包下的两个集合上级接口

  • Collection:存储单个对象的集合 特点:无序(有序、无序是指在进行插入操作时,插入位置的顺序性
    先插的位置在前,后插的位置在后,则为有序,反之无序
    ),不唯一

    如图所示,它拥有三个子类型集合ListSetQueue(重点是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

集合的遍历

  1. 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);		}	}}
  1. 使用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);
}
  1. 使用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是有序的

泛型

  1. 为什么要用泛型:先注意集合的特点:集合长度可变,并且可以放任意的引用数据类型,存储的数据类型也可以不一样假如我有一个这样的需求,我需要获取全班所有人的姓名,但有个别人捣乱,把名字写成电话号码,导致我后面的代码出错,这种时候,就需要使用到泛型了,如果没按照我的规定来存放数据就会报错,等于是将运行时期会发生的异常提前到编译时期了。所以可见泛型是一种安全机制,一种规范,它和接口一样在制定规则。

  2. 如何理解泛型?大家上学的时候都写过作文吧,考试的时候作文上面的要求经常是题材不限,但是一旦你确认题材了,你就不能再写其他题材的作文了,泛型也是如此,本质上泛型是一个变量,它是需要我们赋值的。

//例如
List<student> names = new ArrayList<student> names();
//这时我如果给他存放其他的数据类型就会出错  而且我们在取元素的时候不需要强制类型转换

还有泛型类泛型方法之类的没太多时间去补充,到时候补充和完善。

posted @ 2021-11-13 12:18  一个小菜鸟的进阶之路  阅读(100)  评论(8编辑  收藏  举报