面向对象&面向过程
面向过程思想:侧重过程,第一步做什么,第二步做什么...比如喝水,我们用面向过程思想分析就是先拿起杯子,然后喝水,最后放下杯子。
面向对象思想:侧重对象,物以类聚,先创建一个大类,然后实例化这个类,这些实例就是一个一个的对象。比如老师是一个大类,在这个类中会有李老师,王老师等具体的一个人,这些具体的某个人就是对象。
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但具体到微观操作,仍需要面向过程的思路去处理。
什么是面向对象?面向对象的本质就是以类的方式组织代码,以对象的组织(封装)数据。其具有封装、继承、多态三大特性。
类和对象
类是一种抽象的数据类型,是对某一类事物的整体描述,但不能代表一个具体的事物。
对象是抽象概念的具体实例。能体现出特点,展现出功能的具体实例。
创建和初始对象
使用new来创建一个对象
使用new关键字创建时,除分配内存空间外,还会给创建好的对象进行默认初始化以及对类中构造器的调用。
类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的。有两个特点:
1.必须和类的名字相同
2.没有返回类型,void也不可以
public class Phone {
//属性
String name;
double price;
//方法
public void music(){
System.out.println(this.name + "在放歌");
}
}
以上代码创建了一个Phone手机类,定义了名称和价格两个属性和music方法。
public class PhoneTest {
public static void main(String[] args){
Phone p1 = new Phone(); //创建一个新对象
p1.name = "华为";
p1.price = 4399.0;
System.out.println("手机名称:" + p1.name);
System.out.println("手机价格:" + p1.price);
p1.music();
}
}
属性:也就是成员变量;
默认初始化:
数字:int 0 浮点型 0.0
char:u0000
boolean:false
引用类型:null
修饰符 属性类型 属性名 = 属性值
构造器
public class Person {
//一个类即使什么都不写,也会存在一个方法
//显示的定义一个构造器
public Person(){
}
}
public class PersonTest {
public static void main(String[] args) {
//使用new实例化一个对象
//使用new关键组字本质是在调用构造器,也用来初始化
Person p = new Person();
}
}
public class Person {
String name;
//无参构造
public Person(){
}
//有参构造:一旦定义了有参构造,无参就必须显式定义
public Person(String name){
this.name = name;
}
//快速插入构造方法:Alt+Insert(Ins键)
}
创建对象内存分析
栈:由操作系统自动分配释放,用于存放函数的参数值、局部变量等。其中函数中定义的局部变量是按照定义的先后顺序依次放入栈中,也就是说相邻变量的地址之间不存在其他变量。并且栈的内存空间是从下到上开辟,内存地址由高到低,先定义的地址高于后定义的。形象的理解一下就是:把栈看作是装乒乓球的桶,而局部变量是乒乓球,我们要把球放进去就要一个一个依次放,先放的先拥有一个空间并且越靠前。
栈中储存的数据的生命周期随函数的执行完成而结束。
堆:由开发人员分配和释放,若开发人员不释放,程序结束时由OS(操作系统)回收。堆的内存地址与栈相反,由低到高,后申请的内存空间不一定在先申请的后面,如果先申请的空间释放后,后申请的也可以占用前面释放的空间。
public class Pet {
public String name;
public int age;
public void shout(){
System.out.println("叫了一声");
}
}
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
System.out.println(dog.name);
System.out.println(dog.age);
dog.shout();
Pet cat = new Pet();
}
}
例如以上代码的内存图就是:
封装
该露的露,该藏的藏:就是把代码的细节藏起来,把我们需要展示的东西露给用户。比如电视机就是把电视机内部的构造和细节藏起来,只让用户看到开关、接口和屏幕画面。
我们的程序设计要追求“高内聚,低耦合”。高内聚指的是内部数据操作细节自己完成,不允许外部干涉;低耦合指仅暴露少量的方法给外部使用。
属性私有:get/set
//类 private:私有,把属性私有,无法操控这个属性
public class Student {
//属性私有
private String name; //名字
private int id; //学号
private char sex; //性别
//要想操控私有属性,我们需要提供一些可以操作这个属性的方法
//就是提供一些public的get set方法(alt+insert)
//get获得这个数据
public String getName(){
return this.name;
}
//set给这个数据设置值
public void setName(String name){
this.name = name;
}
}
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("王小明");
System.out.println(s1.getName());
}
}
封装的意义:
1.提高程序安全性,保护数据
2.隐藏代码的实现细节
3.统一接口
4.系统可维护增加了
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
关键字:extends,意思是扩展,子类是父类的扩展。
继承是类和类之间的一种关系,除此之外还有依赖、组合、聚合等。
继承关系的两个类,一个是子类(派生类),一个是父类(基类),使用extends来表示
子类和父类之间从意义上讲应该具有“is a”的关系("子类"是一个"父类")
子类可以继承父类的所有东西,除私有的以外!被final修饰的类,无法被继承(断子绝孙)
父类:
public class Person {
public void say(){
System.out.println("说了一句话");
}
}
子类:
public class Student extends Person{
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.say();
}
}
运行结果:
快捷键:alt+H
在Java中所有类都默认直接或间接继承Object类
super和this
//父类
public class Person {
public String name = "李子明";
}
//子类
public class Student extends Person{
private String name = "小红";
public void test(String name){
//这个name指传入的参数
System.out.println(name);//小黄
//this指该类中定义的name
System.out.println(this.name);//小红
//父类的name
System.out.println(super.name);//李子明
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.test("小黄");
}
}
调用父类构造器必须放在子类构造器的第一行
在子类代码中,会默认调用父类的方法,对于无参构造来说,我们写出来就是用super()来表示,不写这句就相当于是隐藏代码。如果直接写有参构造,无参构造就会消失,使得子类无法调用父类的无参构造,那么我们在调用的时候就要写出super语句来调用父类的有参构造。
注意点:
1.super调用父类的构造方法,必须在构造方法第一个
2.super必须只能出现子类的方法或构造方法中
3.super和this不能同时调用构造方法
super与this的区别:this指本身调用者这个对象,super代表父类对象的应用;this没有继承也可以用,super只能在继承条件下使用;this()是本类构造,super()是父类构造。
方法的重写
重写都是方法的重写,与属性无关!
public class B {
public static void test(){
System.out.println("hello~");
}
}
public class A extends B {
public static void test(){
System.out.println("你好");
}
}
public class Test {
public static void main(String[] args) {
A a = new A();
a.test(); //你好
B b = new A();
b.test(); //hello~
}
}
上述代码A,B两个类都是static类型的,因此Test类中两次调用的test方法只和左边的定义的数据类型有关,和new后面的无关,导致最后运行结果输出不同。把static删除后,两次的输出结果是一致的,这就是子类重新了父类的方法,也就是说,重写是和非静态方法有关的。重新的关键词必须是public!
总结:重新要有继承关系,子类重写父类的方法。方法名必须相同;参数列表必须相同;修饰符:范围可以扩大但不能缩小(public>Protected>Default>private)。
多态
多态即同一个方法可以根据发送对象的不同而采用多种不同的行为方式。一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多。
多态存在的条件:
1.有继承关系
2.子类重写父类方法:static,final,private不能重写
3.父类引用指向子类对象(Father f1 = new Son())
//父类
public class Person {
public void run(){
System.out.println("在跑步");
}
}
//子类
public class Student extends Person {
//重写了父类的run方法,最终会执行重写后的,也就是输出“run”
public void run(){
System.out.println("run");
}
}
//测试类
public class Test {
public static void main(String[] args) {
//一个对象的实际类型是确定的
//new Student() Student类型
//new Person() Person类型
//可以指向的引用类型就不确定了,父类的引用指向子类
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
//对象能执行哪些方法主要看对象左边的类型,和右边关系不大
s2.run(); //run
s1.run(); //run
}
}
注意:父类可以指向子类,但是不能不能调用子类独有的方法
instanceof
用来判断两个类之间有没有父子关系
//Person是父类,Teacher和Student是子类
public class Test {
public static void main(String[] args) {
Object o = new Student();
System.out.println(o instanceof Student);//true
System.out.println(o instanceof Person);//true
System.out.println(o instanceof Object);//true
System.out.println(o instanceof Teacher);//false
//String是无关类
System.out.println(o instanceof String);//false
}
}
类型转换
public class Person {
}
public class Student extends Person {
public void go(){
System.out.println("Let's go~");
}
}
public class Test {
public static void main(String[] args) {
Person o = new Student();
//o.go(); 报错!因为o是Person类型的,Person中没有go方法,无法调用
((Student) o).go();//将o转换为Student类型
}
}
子类转换为父类,可能丢失自己的本来一些方法
父类引用指向子类的对象,类型转换方便方法的调用,减少重复的代码。
static关键字
静态变量和静态方法于类,所有对象都共享
public class Student {
private static int age;//静态的变量
private double score;//非静态的变量
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);
// System.out.println(Student.score);----报错。
System.out.println(s1.score);
}
}
对于方法来说,非静态方法可以调用静态方法。
静态代码块:第一个执行,和类一起加载,只执行一次,可用其初始化
//匿名代码块
{
}
//静态代码块
static{
}
抽象类
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,该类就是抽象类。
抽象类中可以没有抽象方法,但是有抽象方法的类一点要声明为抽象类。
抽象类不能用new关键字来创建对象,它是用来让子类继承的。
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
子类继承抽象类,就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
public abstract class Person {
//抽象方法,只有方法名字,没有方法的实现
public abstract void doSomething();
}
抽象类是有构造器的,可以派生子类!
接口
类只能单继承,接口可以多继承
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能...”的思想。
接口的本质是契约,就像法律一样,制定好大家都遵守。
public interface UserService {
//接口中的所有定义其实都是抽象的 public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
public interface TimeService {
void timer();
}
//类可以实现接口 implements 接口
//利用接口实现多继承
public class UserServiceImpl implements UserService,TimeService{
@Override
public void add(String name){
}
@Override
public void delete(String name){
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
接口中也可以定义常量(public static final),不常用
注意:接口中没有构造方法,所有不能被实例化;implements(实现类)可以实现多个接口;实现类必须要重写接口中的方法。
异常
软件程序在运行过程中,非常可能遇到一些异常问题,我们就叫异常,英文是Exception。异常指程序运行中出现的不期而至的各种状况。如:文件找不到、网络连接失败、非法参数等。
三种类型:
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。
运行时异常:它是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误(error):错误不是异常,而是脱离程序员控制的问题,错误在代码中通常被忽略。
异常体系结构:
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
在Java APL中已经定义了许多异常类,这些异常类分为两大类,错误(error)和异常(exception)。
error和exception的区别:
error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机一般会选择终止线程;exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
异常处理机制(捕获和抛出)
异常处理五个关键字:try、catch、finally、throw、throws
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
//假设要捕获多个异常,从小到大排列
try{ //try监控区域
System.out.println(a/b);
} catch (ArithmeticException e){ //catch捕获异常,catch(想要捕获的异常类型!)
System.out.println("程序出现异常,变量b不能为0");
}finally { //处理善后工作,一定会执行
System.out.println("finally");
}
//finally可以不要
}
}
public class Test {
public static void main(String[] args) {
new Test().test(1,0);
}
//假设这个方法中,处理不了这个异常,方法上抛出异常
public void test(int a,int b){
if (b==0){
throw new ArithmeticException();//主动抛出异常,一般在方法中
}
}
}
运行结果:
自定义异常
用户自定义异常,只需要继承Exception类就可以了。
步骤:
1.创建自定义异常
2.在方法中通过throw关键字抛出异常对象
3.如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
4.在出现异常方法的调用者中捕获并处理异常
public class MyException extends Exception {
private int detail;
public MyException(int a) {
this.detail = a;
}
public String toString(){
return "MyException{" + detail + '}';
}
}
public class Test {
static void test(int a) throws MyException{
System.out.println("传递的参数为:" + a);
if (a>10){
throw new MyException(a);//抛出
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
}catch (MyException e){
System.out.println("MyException:" + e);
}
}
}