Java面向对象
一、类和对象
1.概念
类是一类事物的统称,如人类、鸟类等;类是构造对象时所一类的规范,对象则是类的其中一种特定实现,如鸟类中又有麻雀、燕子、海鸥等,类中包含了所有鸟类的相同属性和行为,在Java中表现为属性和成员方法。
2.命名规范
类名由多个单词组成时,每个单词首字母大写;命名成员变量/属性时,从第二个单词起每个首字母大写;成员方法命名方法与变量命名一致。
3.创建对象
使用new关键字,调用类的构造函数创建类的对象。对象存储在内存空间中的堆。
4.成员变量和成员方法
(一)成员变量的初始值
数值类型的初始值都为0,布尔型默认值为false,引用类型为null.
(二)成员方法
class Person{
String name;
String sex;
int age;
public void action(){
System.out.println("吃饭睡觉打豆豆!");
}
private String info(String name,String sex,int age){
//这里的this表示当前类的属性
this.name=name;
this.sex=sex;
this.age=age;
}
}
(三)返回值类型
返回值主要指从方法当中返回到方法体外的数据内容,可以是基本类型中的一种,也可以是引用类型。当该方法不需要返回任何数据内容时,返回值类型写void即可。
return 关键字可以返回具体的数据内容并结束当前方法。
public String info(int age){
return "这是返回的内容!";
}
(四)形参和实参以及可变实参
一个方法的形参列表中最多只能声明一个可变长形参,并且需要放到参数列表的末尾。
class Test{
int age;
String name;
public String info(int age){ //这里的age为形参
return "这是返回的内容!"+age;
}
public int time(int num,String... a){
System.out.println(num);
for(int i=0,i<a.length;i++){
System.out.println("第"+i+"个元素"+a[i]);
}
}
}
public class Demo{
public static void main(String[] args){
Test t=new Test();
t.info(18);//这里的18为实参
}
}
(五)参数传递注意的问题
1.基本数据类型的变量作为方法的参数传递时,形参变量数值的改变通常不会影响到实参变量的数值,因为两个变量有各自独立的内存空间;
2.引用数据类型的变量作为方法的参数传递时,形参变量指向内容的改变会影响到实参变量指向内容的数值,因为两个变量指向同一块内存空间
3.当引用数据类型的变量作为方法的参数传递时,若形参变量改变指向后再改变指定的内容,则通常不会影响到实参变量指向内容的改变,因为两个变量指向不同的内存空间
(六)内存结构之栈区
程序运行过程中所有的局部变量都被存储在栈中,JVM会为每一个被调用的方法分配一个与之对应的空间,该空间叫做栈帧。一个栈帧对应一个正在调用中的方法,栈帧中存储了方法的参数和局部变量等数据。方法调用结束后,对应的栈帧将会被消除。
三、方法
1.构造方法
class Bird{
public Bird(){默认无参构造函数
//代码块
}
}
public class Demo{
public static void main(String[] args){
//使用new关键字创建对象时,会自动调用匹配的构造函数
Bird bird=new Bird();
}
}
2.方法的重载
具有相同的方法名,不同的参数列表
class Calculate{
//返回值类型最好是相同的
public int add(int a,int b){
return a+b;
}
public int add(int a,double b){
return a+b;
}
public int add(double a,double b){
return a+b;
}
public int add(String a,String b){
return a+b;
}
}
重载的意义:便于调用者记忆和使用。根据不同情况传入不同的实参。
3.方法的重写(Override)
(一)概念
从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式就叫做方法的重写
(二)重写的原则
- 要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许返回子类类型。
- 要求方法的访问权限不能变小,可以相同或者变大。
- 要求方法不能抛出更大的异常(异常机制)
4.方法递归调用(***)
(一)递归的概念
递归本质就是指在方法体的内部直接或间接调用当前方法自身的形式。
(二)注意事项
- 使用递归必须有递归的规律以及退出条件。
- 使用递归必须使得问题简单化而不是复杂化。
- 若递归影响到程序的执行性能,则使用递推取代之。
四、this关键字
1.概念
若在构造方法中出现了this关键字,则代表当前正在构造的对象。
若在成员方法中出现了this关键字,则代表当前正在调用的对象。
this关键字本质上就是当前类类型的引用变量。
2.工作原理
在构造方法中和成员方法中访问成员变量时,编译器会加上this.的前缀,而this.相当于汉语中"我的",当不同的对象调用同一个方法时,由于调用方法的对象不同导致this关键字不同,从而this.方式访问的结果也就随之不同。
3.使用方式
当局部变量名与成员变量名相同时,方法中要访问成员变量需要在成员变量名前加this。
//这里以setXxx()方法为例子
public String setName(String name){
//this.name表示成员变量name
this.name=name;//用形参赋值给成员变量
}
this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以作为方法的返回值。如 return this;
在构造方法的第一行可以使用this()的方式来调用本类中的其它构造方法(套娃操作)。
五、static关键字
1.概念
- 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,也就是整个类只有一份并被所有对象共享,该成员变量随着类的加载准备就绪,与是否创建对象无关。
- static关键字修饰的成员可以使用引用.的方式访问,但推荐类名.的方式
2.使用方式
- 在非静态成员方法中既能访问非静态的成员又能访问静态的成员,(成员:成员变量 + 成员方法, 静态成员被所有对象共享)
- 在静态成员方法中只能访问静态成员不能访问非静态成员。
- 在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用static关键字修饰。(不能滥用static关键字)
3.构造块和静态代码块
- 构造块:在类体中直接使用{}括起来的代码块。
- 每创建一个对象都会执行一次构造块。
- 用于统一初始化成员变量
- 静态代码块:使用static关键字修饰的构造块。
- 静态代码块随着类加载时执行一次,也就是说执行顺序先于构造块
- 如 加载数据库的驱动包
public class Demo{
{
System.out.println("---这是非静态构造块!---");
}
static {
System.out.println("---这是静态构造块!---");
}
public Demo(){
System.out.println("---这是无参构造函数!---");
}
public Demo(String name){
System.out.println("---这是有参构造函数!---");
}
}
在继承关系中:
- 先执行父类的静态代码块,再执行子类的静态代码块。
- 执行父类的构造块,执行父类的构造方法体。
- 执行子类的构造块,执行子类的构造方法体
六、单例设计模式
1.概念
在某些特殊场合中,一个类对外提供且只提供一个对象时,这样的类叫做单例类,而设计单例的流程和思想叫做单例设计模式。
2.实现流程
- 私有化构造方法,使用private关键字修饰。
- 声明本类类型的引用指向本类类型的对象,并使用private static关键字共同修饰。
- 提供公有的get方法负责将对象返回出去,并使用public static关键字共同修饰。
public SingleDemo{
//饿汉式
private static SingleDemo s=new SingleDemo();//一个类只有一份
private SingleDemo(){} //使用private关键字修饰
//提供public方法访问
public SingleDemo getInstance(){
return s;
}
//懒汉式
private static SingleDemo s=null;
private SingleDemo(){} //使用private关键字修饰
//提供public方法访问
public SingleDemo getInstance(){
if(null==s){
s=new SingleDemo();
}
return s;
}
}
两者区别在于饿汉式是调用getInstance()方法前就已经创建了对象,懒汉式是在调用方法后创建的类对象。
3.实现方式
单例设计模式的实现方式有两种:饿汉式 和 懒汉式,在以后的开发中推荐饿汉式。
七、面向对象特性
1.封装、继承、多态
(一)封裝
隐藏具体的实现细节,保证内部不受破坏。
成员变量用private修饰,即成员变量私有化。
生成共有的getXxx()、setXxx()方法来从外部访问和修改成员变量。
(二)继承
概念
java中使用extends关键字来表示继承关系:
public class Son extends Father{
/*
Father类叫做超类、父类、基类。
Son类叫做派生类、子类、孩子类。
*/
}
使用继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件。
特点
- 子类无法继承父类的构造方法和私有方法,但私有成员变量可以被继承只是无法直接访问。
- 无论使用何种方式构造子类的对象时都会自动调用父类的无参构造方法,来初始化从父类中继承的成员变量,相当于在构造方法的第一行增加代码super()的效果。
- 必须满足逻辑关系,如父亲无法继承儿子
- java中一个类只能继承一个类,即单继承。
(三)多态
概念:同种事物具有多种表现形态。例如在Java中动物类包含了鸟类、爬虫类、哺乳类等。
语法格式:父类类型 引用名=new 子类类型();
如 Animal a=new Bird();
Animal a=new Dog();
示例
题目描述:
1.编程实现Shape类的封装,特征有:横纵坐标,要求提供打印所有特征的方法。
2.编程实现Rect类的封装并继承自Shape类,特征有:长度和宽度。
3.编程实现ShapeRectTest类,在main方法中分别创建Shape和Rect类型对象并打印特征。
shape类
public class Shape {
public Shape(){}
public Shape(int x,int y){
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
private int x;
private int y;
public void show(){
System.out.println("Shape横坐标:"+x+"\t纵坐标:"+y);
}
}
Rect类
public class Rect extends Shape{
private int width;//矩形宽
private int length;//矩形长
public Rect(){}
public Rect(int x,int y,int w,int l){
super(x,y);
setWidth(w);
setLength(l);
}
@Override
public void show() {
System.out.println("Rect矩形宽:"+width+"\t矩形长:"+length+"\tX:"+getX()+"\tY:"+getY());
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
ShapeRectTest类
public class ShapeRectTest {
public static void main(String[] args) {
Shape shape=new Shape(10,16);
Rect rect=new Rect(21,10,10,10);
shape.show();
rect.show();
Shape s=new Rect();
s.show();
}
}
输出如下所示:
特点
- 当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法。
- 当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接调用子类独有的方法。
- 对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)。
- 对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本。
示例:`
//父类
public class Father{
public void smoke(){//父亲独有的方法
System.out.println("父亲没事就点个烟抽~");
}
public static void show(){
System.out.println("父类的静态成员方法");
}
}
//子类
public class Son extends Father{
public void cry(){
System.out.println("儿子没奶吃就开始哭");
}
public static void show(){
System.out.println("子类的静态成员方法");
}
public static void main(String[] args){
Father f=new Son();
f.smoke();
//f.cry(); 父类引用指向子类对象 是无法调用子类独有方法的
((Son)f).cry();//强制类型转换后可以调用子类独有的方法
//静态方法也可以使用 对象名+"."的方式调用 但推荐使用类名.的方式调用
f.show();//输出结果为父类的静态方法
}
}
输出结果:
父亲没事就点个烟抽~
父类的静态成员方法
引用数据类型的转换(向上转型和向下转型)
引用数据类型的转换分为自动类型转换和强制类型转换,有时也称作向上转型和向下转型。
1.自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也叫做向上转型。
2.强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也叫做向下转型或显式类型转换。
3.引用数据类型之间的转换必须发生在父子类之间,否则编译报错。 即不具有继承关系的类之间的转换时错误的。
4.若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常。
如下所示:
public class Father{
//代码块
}
public class Son extends Father{
//代码块
public static void main(String[] args){
Father f=new Son();
//引用变量f 实质上是 Son类型的引用变量,即真正指向的数据类型
Son s=(Son)f;
//
}
}
5.为了避免上述错误的发生,应该在强转之前进行判断,格式如下:
if(引用变量 instanceof 数据类型)判断引用变量指向的对象是否为后面的数据类型。
多态的实际意义
在于屏蔽不同子类的差异性实现通用的编程带来不同的
效果。
八、abstract关键字
1.抽象方法概念
抽象方法主要是指使用abstract关键字修饰,没有方法体。
public abstract void show();
2.抽象类概念
不能实例化的即无法创建对象,且使用abstract关键字修饰的类。
示例:
public abstract AbstractDemo{
private int ab;//成员变量
public void test(){//成员方法
//代码块
}
//抽象方法
public abstract void show();
}
3.抽象类和抽象方法的关系
1.抽象类中可以有成员变量、构造方法、成员方法;
2.抽象类中可以没有抽象方法,也可以有抽象方法;
3.拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是4.具有抽象方法并且使用abstract关键字修饰的类。
4.抽象类的实际意义
抽象类的实际意义不在于创建对象而在于被继承。当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式。
九、接口
1.概念
接口是特殊的抽象类,接口的所有方法均为抽象方法。使用interface关键字定义接口。
2.类和接口的关系
- 类和类之间的关系 使用extends关键字表达继承关系,支持单继承
- 类和接口之间的关系 使用implements关键字表达实现关系,支持多实现
- 接口和接口之间的关系 使用extends关键字表达继承关系,支持多继承
3.抽象类和接口的区别
- 定义关键字不同,抽象类为abstract,接口为interface
- 抽象类需要被继承,用extends关键字;而实现接口使用implements关键字
- 抽象类中既可以有抽象方法,也可以有非抽象方法;而接口只可以有抽象方法
- 抽象类中可以有成员变量,而接口只可以定义常量
- 抽象类有构造方法,而接口没有
- 在Java 8以前,抽象类中增加方法时,子类可以不用重写;而接口中增加方法时,其实现类必须重写新增的方法。
- Java 8版本开始,接口中允许出现非抽象方法和静态方法,但非抽象方法需使用default关键字修饰
- Java 9 新特性,接口中允许出现私有方法,可以用于减少内部代码冗余。