Java学习之路(四):面向对象
Java中的面向对象
概念:面向对象的原本的意思是“”万物皆对象“”
面向对象思想的特点:
- 是一种更符合我们思想习惯的思想,将复杂的事情简单化
- 使我们角色发生了转换,将我们从执行者变成了指挥者
面向对象的特征:
- 封装(Encapsulation)
- 继承(Inheritance)
- 多态(Polymorphism)
一些专业性的词汇:
- 面向对象的分析(OOA,Object Oriented Analysis)
- 面向对象的设计(OOD,Object Oriented Design)
- 面向对象的编程实现(OOP,Object Oriented Programming)
面向对象的开发或者说面向对象的编程的本质就是不断的创建对象,使用对象,指挥对象做事情,管理和维护对象之间的关系
面向对象的基本概念
对象就是指人们要进行研究的世界上的任何事物,从最简单的整数到复杂的飞机...均都可以看做是一个对象,它不仅仅能表示具体的事物,还能抽象的规则、计划或事件
如何描述现实世界的事物
- 属性:就是该事物的描述信息(事物身上的名词)
- 行为:就是该事物能够做什么(事物身上的动词)
Java中最基本的单位是类,Java中是用class来描述一件事物
-
类中有成员变量,就是事物的属性
-
类中有成员方法,就是事物的行为
其实我们学习java是学些什么?就是学习面向对象
声明类
声明成员变量
声明成员方法
创建对象
给属性赋值
使用对象
类和对象的概念
- 类:是一组相关的属性和行为的集合
- 对象:是该类事物的具体体现
- 注意:其实一个class类实例化后就是一个对象了
类、属相和方法的声明
- 类的声明:class关键字,eg:class Student
- 属性的声明:数据类型 属性名,eg:String name
- 方法的声明:public 返回值类型 方法名(){ }
//声明一个学生类 //属性:姓名,年龄,性别 //行为:学习,睡觉 class Student{ String name; int age; boolean sex; public void study(){ System.out.println("这里是学习方法") } public void sleep(){ System.out.println("这里是睡觉方法") } }
类的使用
- 创建对象:类名 对象名 = new 类名();
- 给属性赋值:对象名.变量名
- 调用方法:对象名.方法名()
Java中的垃圾回收机制
Java会自动回收无用的对象占用的内存空间,使该空间可以被程序再次使用,程序员不需要向C语言一样要自己进行释放内存的空间,CFRelease()
成员变量和局部变量之间的区别
- 在类中的位置不同
- 成员变量:在类之中,方法之外
- 局部变量:实在方法中定义的
- 在内存中的位置不同
- 成员变量:在堆内存(成员变量属于对象,对象进堆内存)
- 局部变量:位于栈内存(局部变量属于方法,方法进入栈内存)
- 声明周期不同
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:对着方法的调用而存在,随着方法的调用完毕而消失
- 初始化的值不同
- 成员变量:有默认的初始化值
- 局部变量:没有默认的初始化值,必须定义,赋值然后才能使用
成员变量和局部变量的注意事项:
局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则
基本数据类型(传值)和引用数据类型(传地址)有哪些?
1.基本数据类型:byte,short,int,long,float,double,boolean,char
2.引用数据类型:数组(eg:int[]),类(class),接口(interface),枚举
方法的形式参数是一个类名的适合如何调用?
如果你看到一个方法形式参数是一个类类型(引用类型)的话,那么调用方法的时候其实需要的是该类的对象。
匿名对象
什么是匿名对象:没有给新创建的对象储存在一个变量里
应用的场景:
1.调用方法,仅仅只需要调用一次的时候、
2.匿名对象可以作为实际的参数进行传递
封装概述:
是指隐藏对象的属性和实现细节,仅仅对外提供公用的public访问方式
封装好处:
- 隐藏实现的细节,提供公共的访问方式
- 提高了代码的复用性和安全性
封装原则:
- 封装原则将不需要对外提供的内容都隐藏起来
- 把属性隐藏,提供公共的方法对其访问
Private(私有)关键字
- 是一个权限修饰符
- 可以修饰成员变量和成员方法
- 被其修饰的成员只能在本类中被访问
当成员变量用private修饰表示外界不能访问
this关键字的特点
- 代表当前对象的引用
this的应用场景
- 用来区分成员变量和局部变量的重名
this是和外面调用的变量指向同一个地址的,指的是同一对象。
class Student{ String name; int age; public Student(){} public Student(String name,int age){ this.name = name; this.age = age; } }
像这样,再构造方法中的应用就是最好的例子。
如果你不加this,你可以试一下,会有很好玩的事情发生。
构造方法Constructor的概述
构造方法的作用:给对象的数据(属性)进行初始化的一个赋值
构造方法的特点:
- 方法名与类名相同(大小也与类名一致)
- 没有返回值类型,注意:void也不要写,什么都不写
- 没有具体的返回值return
构造方法分为
- 有参构造方法
- 无参构造方法
构造方法的重载:
是指方法名相同,与返回值类型无关(构造方法没有返回值),只看参数列表
构造方法的注意事项
- 如果我们没有给出构造方法,那么系统会默认给出一个无参数的构造方法
- 如果我们给出了构造方法,系统将不在提供默认的无参构造方法
- 如果我们声明的有参构造方法,这个时候。如果我们还想使用无参构造方法,就必须自己给出。建议永远自己给出无参构造方法。
- 正确的构造方法的格式【public 类名()】
static关键字
static关键字的特点:
- 随着类的加载而加载
- static修饰的东西是优先于对象的存在
- 被类的所有对象共享
共性用静态,特性用非静态
static声明的成员属性可以通过类名进行调用
- 其实其本身也可以通过实例化的对象进行调用
- 推荐使用类名进行调用
- 静态修饰的内容我们一般称之为:与类相关的类成员
注意事项:
- static可以用来修饰属性,也可以用来修饰方法
- 在静态方法中是没有this关键字的
- 静态是随着类的加载而加载的,this则是随着对象的创建而进行创建的
- 静态比对象要优先
- 静态方法只能访问静态的成员变量和静态的成员方法
- 非静态的方法可以访问静态的成员变量和静态的成员方法
静态变量和成员变量的区别 1.静态变量又叫类变量,成员变量又叫对象变量 2.所属不同: 静态变量属于类() 成员变量属于对象,所以也称之为实例变量(对象变量) 3.内存中的位置不同 静态变量属于类,在方法区的静态区 成员变量存储与堆内存 4.内存出现时间不同 静态变量随着类的加载而加载,随着类的消失而消失 成员变量随着对象的创建而存在,随着对象的消失而消失 5.调用不同 静态变量可以通过类名调用,也可以通过对象调用 成员变量只能通过对象调用
代码块的概述:使用{}括起来的代码就称之为代码块
代码块的分类:
根据其位置和声明的不同,可以分为:
- 局部代码块
- 构造代码块
- 静态代码块
- 同步代码块(多线程)
局部代码块:在方法中出现,限定变量的生命周期,及早释放,提高内存的利用率
构造代码块:在类中方法外出现,多个构造方法中相同代码存放到一起,每次调用构造都执行,并且在构造方法前执行(注意了,构造代码块和构造方法可不是同一种东西)
静态代码块(最常用):在类中方法外出现,并且要加上static修饰,用于给类进行初始化,在加载的时候就执行,并且只执行一次,一般用于加载驱动
//局部代码块 public class null01 { public static void main(String[] args) { // TODO Auto-generated method stub { int a = 1; System.out.println(a); }//代码块执行结束,变量a被释放 } }
//构造代码块
class Student{ String name; int age; public Student(){ name = "null"; age = 20; } { System.out.println("这就是构造代码块"); } } //每次都会执行,而且是优先于构造方法执行的
//静态代码块 class Student{ String name; int age; public Student(){ name = "null"; age = 20; } { System.out.println("这就是构造代码块"); } static{ System.out.println("这里是静态代码块"); }//在加载的时候就会执行,而且只会执行这一次。一般用于加载驱动 }
继承概述:让类与类之间产生关系,子父关系
继承的好处:
- 提高代码的复用性
- 提高代码的维护性
- 让类与类之间产生了关系,继承是多态的前提
继承的弊端:
- 类的耦合性增强了
- 开发的原则:高内聚(自己完成某事),低耦合
继承的特点:
- Java只支持单继承,不支持多继承(关键字 extends)
- Java支持多层继承
- 如果想用这个体系的所有功能,我们有最底层的类来创建对象
- 如果想用这个体系的共性功能,我们调用最顶层的类
继承的注意事项:
- 子类只能继承父类所有的非私有的成员(非私有的成语方法和成员变量)
- 子类可以通过super关键字访问父类的东西
super和this
- super的作用:super指的是当前类的对象的父类引用
- super可以调用父类的成员属性 super.成员变量
- super可以调用父类的成员方法 super.成员方法
- super可以调用父类的构造方法 super(...)
- .
- this的作用:this指的是当前类的对象引用
- this可以调用本类的成员属性
- this可以调用本类的成员方法
- this可以调用父类的成员方法(在本类没有的情况下) this.父类方法、this.父类属性
- this可以调用本类的构造方法
继承中的构造方法的关系
子类中的所有的构造方法默认都会访问父类中空参数的构造方法
应为子类会继承父类中的数据,可能还会使用父类的数据,所以在子类的初始化之前,一定要用super首先要完成父类的初始化
所以我们会发现,当我们利用Eclipse工具生成子类的构造方法时就会存在super()方法
继承工构造方法的注意事项
- 子类必须创建无参构造方法,内还要调用父类的构造方法
- 子类实现有参构造方法
- 父类自己实现无参构造方法
- super()和this都是必须出现在构造方法的第一条语句上
方法的重写
指的是子类中出现和父类一模一样的方法。重写这个概念只存在与继承的父子之间
应用场景:
当子类需要父类的功能,而子类又有自己的特有内容的时候,可以重写父类中的方法。这样就可以即沿袭了父类的功能,有定义了子类特有的内容
注意事项:
父类中私有方法不能重写,因为父类私有方法子类根本就无法继承
子类重写父类的时候,访问权限最好一致
方法的重写与重载
Overload是指方法的重载,重载可以改变返回值类型,方法的重载只看参数类表不同
Override是指方法的重写,也就是子类中出现了和父类中方法声明一模一样的方法
方法的重写与返回值类型有关,返回值是一致的(或者是子父类)的
方法的重载,在本类中出现的方法名相同,参数列表不同。与返回值类型无关
Final关键字修饰类,方法以及变量
final修饰的特点:
- 修饰类,类不能被继承
- 修饰变量,变量就变成了常量,只能被赋值一次
- 修饰方法,方法就不能被重写
修饰变量:
- final修饰变量叫常量,一般会与public static公用
- 注:常量的变量名一定要全部大写
final修饰局部变量:
- 修饰基本类型,值不能被改变
- 修饰引用类型,是地址不能被改变,对象中的属性可以改变
- 修饰引用类型,那就不可以在new对象了
多态的概述
什么是多态:事物的多种状态...
Java中存在多态的前提条件:
- 要有继承关系
- 要有方法的重写
- 要有父类引用指向子类对象(Father son = new Son())
多态中的成员访问特点:
- 成员变量:编译看左边(父类),运行看左边(父类)
- 成员方法:编译看左边(父类),运行看右边(子类)
- 静态方法:编译看左边(父类),运行看左边(父类)
- 总结:只有非静态的成员方法,编译看左边,运行看右边
注意:在多态的写法中,父类指向了子类,我们实例化然后调用一个方法是,这个方法在父类中必须存在,否则将会无法进行编译
多态中的向上转型和向下转型
- 向上转型:父类指向子类对象 Father p = new Son();
- 向下转型:子类指向父类对象 Son son = (Son)p;
向下转型注意:父类的真实对象必须是子类对象,否则会有问题
多态的好处与弊端:
- 提高了代码的维护性
- 提高了代码的扩展性
- 但是却不能使用子类的特有属性和行为
抽象类的特点:
- 抽象类和抽象方法必须用abstract关键字修饰
- abstract class 类名{}
- public abstract void eat();
- 抽象类不一定有抽象方法,有抽象方法的类一定是个抽象类或者是接口
- 抽象类不能实例化,要由具体的子类实例化。这也是多态的一种(抽象类多态)
- 抽象类的子类:要么是抽象类,要么重写抽象类中所有的抽象方法
抽象类的成员的特点:
- 成员变量:既可以是变量,也可以是常量。
- abstract不能修饰成员变量,只能修饰类和方法
- 构造方法:抽象类也是有构造方法的,用于子类访问父类数据的初始化
- 成员方法:抽象类的方法可以是抽象的,也可以是非抽象的
抽象类的成员的特性:
- 抽象方法:强制要求子类实现
- 非抽象方法:子类可以继承实现
接口
- 从狭义的角度上将,就是指java中的interface关键字
- 从广义上讲对外提供规则的都是接口
- 接口就是提供对外访问的规则
接口的特点:
- 接口用关键字interface表示 interface 接口名{}
- 类去实现接口用implements表示 class 类名 implements 接口名{}
- 接口不能进行实例化
- 接口的子类可以是抽象类(没有意义),一个具体的类,要重写接口中的所有方法
类与类,类与接口,接口与接口的关系
类与类:
继承关系,只能是单继承,可以多层继承
类与接口:
实现关系,可以单实现,也可以多实现,并且还可以在继承一个类的同时实现多个接口
接口与接口:
继承关系,可以单继承,也可以多继承
抽象类与接口的区别
1.成员区别
- 抽象类可以有成员变量,也可以有常量,构造方法,成员方法(抽象的和非抽象的)
- 接口只能有变量,方法只能是抽象方法
2.关系区别
- 类与类:继承关系,单继承
- 类与接口:实现关系,单实现和多实现均可
- 接口与接口:继承关系,单继承与多继承
3.设计理念的区别
- 抽象类:抽象类中定义的是该继承体系的共性功能
- 接口:接口中定义的是该继承体系的扩展功能
package关键字的概述以及作用
package包的作用:
- 包其实就是文件夹
- 包的作用是将字节码(.class)进行分类存放
- 包的作用可以使在同一个项目中有相同的类
- 包的作用有利于以后功能模块的划分
包的格式的定义:
- 包名一般使用公司域名的倒写
包的类能被其他的包访问,那么这个包一定是public的类,public修饰的类代表其他包可以访问此类
四中权限修饰符:
private,default,protected,public
所谓的权限就是别人是否能访问类、属性、方法
用在类上的修饰符:default,public,final(状态修饰符),abstract(抽象修饰符)
用在成员上的修饰符:权限修饰符(private,default,protected,public),状态修饰符(static,final)
用在构造方法上的修饰符:权限修饰符(private,default,protected,public)
用在成员方法上的修饰符:权限修饰符(private,default,protected,public),状态修饰符(static,final),抽象修饰符(abstract)
protected关键字
表示受保护的
protected修饰的方法只能在当前包中访问,或者由子类访问
内部类
内部类的概述和访问特点
- 内部类的概述:就是在class类的内部再定义一个class类
- 内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有的
- 外部类要访问内部类的成员,必须要去创建对象
内部类的创建语法:
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
注:开发中自己声明的内部类比较少用,用的较多的是集合中的遍历
public class null01内部类 { public static void main(String[] args) { // TODO Auto-generated method stub //创建内部类的对象 Outer.Inner inner = new Outer().new Inner(); inner.test1(); } //在一个类的内部声明类,所以他是一个内部类 // class Student{ // } } //此时声明类在另一个类的外面,所以这是一个外部类 //class Student{ //} //这是一个外部类 class Outer{ int a = 12; private int b = 13; //这是一个内部类 class Inner{ public void test1(){ System.out.println("内部访问外部的一个变量a:"+a); System.out.println("内部访问外部的私有属性b:"+b); } } }
1.私有内部类的使用
注:private修饰的,只能在内部访问,所以就无法使用外部类名.内部类名了
那我们如何使用?
和我们对私有属性的操作类似,在class类的内部声明一个获取方法
public class null02私有内部类 { public static void main(String[] args) { // TODO Auto-generated method stub //权限修饰符:private default protected public //类的成员:属性,方法,内部类 //创建内部类对象 //注:private只能在内部访问,所以无法使用Outer.Inner //私有的内部类,就不能在外部调用了 Insert outer = new Insert(); outer.test(); } } class Insert{ int a = 12; private int b = 13; //这是一个内部类 private class Inner{ public void test1(){ System.out.println("内部访问外部的一个变量a:"+a); System.out.println("内部访问外部的私有属性b:"+b); } } public void test(){ Inner inner = new Inner(); inner.test1(); } }
2.静态内部类的使用
创建静态内部方法对象的语句:
注意:说道对象,那就是new出来的东西了,这个才叫对象
外部类名.内部类名 对象名 = 外部类名.内部类对象;
public class null03静态内部类 { public static void main(String[] args){ //创建静态内部方法对象语法:外部类名.内部类名 对象名 = 外部类名.内部类对象; OuterThree.Inner inner = new OuterThree.Inner(); inner.test1(); } } class OuterThree{ static int a = 12; static int b = 13; //这是一个内部类 static class Inner{ public void test1(){ System.out.println("内部访问外部的一个变量a:"+a); System.out.println("内部访问外部的私有属性b:"+b); } } }
3.调用静态内部类的静态方法
静态内部类的静态方法调用:
外部类.静态内部类,静态内部方法;
public class null04调用静态类的静态方法 { public static void main(String[] args) { // TODO Auto-generated method stub //调用静态内部类的静态方法 OuterFour.Inner.test1(); } } class OuterFour{ static int a = 1; //静态的内部类 static class Inner{ public static void test1(){ System.out.println("这里是静态类的静态方法"); } } }
4.局部内部类
- 局部内部类如果想要访问局部变量,那么局部变量必须用final修饰
- 如果不用final修饰,在某些情况下,可能变量会先弹出,但是局部内部类还在存活
- 局部内部类访问它所在的方法中的局部变量必须用final修饰
- 注意:jdk1.8以上的版本不需要加final代码也不会报错了
public class Demo01 { public static void main(String[] args) { Outer outer = new Outer(); outer.test1(); } } class Outer{ public void test1(){ final int a = 10;//局部变量 class Inner{//局部内部类 public void test2(){ System.out.println(a); } } Inner inner = new Inner(); inner.test2(); } }
5.匿名内部类
- 抽象的或者接口的匿名内部类
- 匿名内部类就是内部类的简化方法
- 写匿名内部类的前提是存在一个抽象内部类或者是接口
- 匿名内部类的实现格式: new 抽象或者是接口名(){重写方法};
- 匿名内部类的本质:是一个继承了该类或者实现了该接口的子类的匿名对象
- 匿名内部类的特点:匿名内部类必须实现接口的所有方法;匿名内部类必须实现抽象类的所有方法
- 匿名内部类的方法调用:就像是普通的对象一样,调用方法即可
public class null05匿名内部类 { public static void main(String[] args){ //不能直接调用抽象类 //可以通过子类去重写抽象类 Dog dog = new Dog(); dog.eat(); //可以通过匿名内部类,其实也是匿名方法 new Animal(){ @Override public void eat(){ System.out.println("这个是匿名内部方法"); } }.eat(); } } //写一个抽象类 abstract class Animal{ public abstract void eat(); } class Dog extends Animal{ @Override public void eat(){ System.out.println("我是....."); } }
public class null06接口的匿名内部类 { public static void main(String[] args) { // TODO Auto-generated method stub //第一中class实现接口的调用 DogSix dog = new DogSix(); dog.jumpFileCircle(); new AnimalSix(){ @Override public void jumpFileCircle() { // TODO Auto-generated method stub System.out.println("annother Jump...."); } }.jumpFileCircle(); AnimalSix annther = new AnimalSix(){ @Override public void jumpFileCircle(){ System.out.println("three jump...."); } }; annther.jumpFileCircle(); } } interface AnimalSix{ //跳火圈 public void jumpFileCircle(); } //之前说接口不能直接new, //如何使用接口、 //1.写一个类,实现接口 class DogSix implements AnimalSix{ @Override public void jumpFileCircle(){ System.out.println("jump..."); } } //2.new一个接口的匿名
补充一道面试题:
package nullnull; interface Inter { void show(); } class Outer{ //补齐代码 } class OutDemo{ public static void main(String[] args){ Outer.method().show(); } } //在控制台输出“Helloworld”
interface Inter { void show(); } class Outer{ //补齐代码 public static Inter method(){ return new Inter(){ @Override public void show(){ System.out.println("Helloworld"); } }; } } class OutDemo{ public static void main(String[] args){ Outer.method().show(); } }