您的位置:首页 > 编程语言 > Java开发

Java:面向对象全面总结

2020-08-16 12:16 169 查看

Java面向对象全面总结

面向对象

今天我们就来聊聊什么是面向对象。
不知道大家有没有看过一个小品,就是宋丹丹说把大象装进冰箱要几步。然后宋丹丹再说只要三步。第一步:打开冰箱门。第二步:把大象装进冰箱。第三步:关上冰箱门。她是这么说的,对吧!那我们别管是怎样把大象装进冰箱的,现在我们再想如何把100,甚至1000,或者更多的大象装进冰箱,如果我们自己去装的话,是不是太累了。所以我们是不是需要招一些人手为我们做这些事情。那我们自己只要指挥就行了。不知道大家通过这个例子理解了没有?我们经常说面向过程和面向对象,这两者的区别是什么,通过我们这个例子其实就可以理解,面向过程就是我们自己亲力亲为去装,那么面向对象就是我们招人手(创建对象)去装,所以面向过程和面向对象的区别是思想的转变,从执行者变成指挥者。

类和对象

面向对象的两个重要的点就是说类和对象。那这俩是什么?干嘛用的?类和对象是有一定的联系,对象对象一定是在有类的情况下创建的。就好比我们想做一辆车?那怎么做出来,我们先是不是需要进行设计,画一个图出来。把车的一些具体形状和一些功能是不是得先设计出来,然后才能根据这个设计出来得图纸才能开始去做,对吧。而类和对象的联系也是这样,类就好比是一张设计的图纸,它只是个概念而已,并不存在,它是很多实物下共有的综合特征。那么对象呢?它就好比是由那张设计的图纸(类)真正做出来的车(对象),所有对象,它是实物,是一个具体存在的东西,它有它自己的个体特征。了解了吗?很形象对吧。那现在跟我创建一个类吧

创建类

权限修饰符 class 类名{
成员属性 ; 方法;
}

创建对象

类名 对象名 = new 类名 ();
对象名.属性 = “内容”;
对象名。方法名();

Java文件里可以存在多个类,但是只能有一个主类。这里涉及到关于类的访问。
主类格式: public 类名{ }

类和对象的创建示范

在这里插入代码片
public class Demo1 {
public static void main(String[] args) {
Car car = new Car();
car.brand = "奥迪";
car.color = "白色";
car.show();
}
}

class Car{
String brand;
String color;
void show() {
System.out.println("汽车品牌:"+brand+",汽车颜色:"+color);
}
}

栈和堆

栈是存放地址的,是后进先出。因为每次new 类名()都会分享内存,这时就会产生一个存放地址。当它赋值给对象名,那就相当于对象名是存放地址的。这样为了方便,如果不要对象名,就会被垃圾回收机制进行释放。如果只执行一次的操作,可以不用对象名,就可以直接用匿名对象 。 如 : new 类名().方法名();同样可以执行相关方法操作。

构造方法

构造方法是为对象一些值进行初始化。这样就可以省一些相关操作。构造方法默认情况下,也就是在你没创建构造方法时,它会自动创建无参无任何语句的一个构造方法。
构造方法创建格式:
类名(形式参数列表){
语句块;
}
方法名和类名相同,就是构造方法。

构造方法的创建示范

在这里插入代码片
class Employee{
String name;          //员工姓名
String tel;           //员工号码
double salary;        //员工基本薪资
double rate;          //员工薪水增长率

//构造类型
Employee() {

}
Employee(String name) {
this.name = name;
}
Employee(String name, String tel) {
this.name = name;
this.tel = tel;
}
Employee(String name, String tel,double salary) {
this.name = name;
this.tel = tel;
this.salary = salary;
}
Employee(String name, String tel,double salary,double rate) {
this.name = name;
this.tel = tel;
this.salary = salary;
this.rate = rate;
}

重载

什么是重载呢?就是说有时候写代码为了见名知意,我们想完成某个功能,比如说是求和,那么我们既想实现两数之和,又想实现三个数之和。那我们就可能需要两个方法,对吧。但是为了见名之意,我们可以把方法名进行重载,就是方法名可以一样,表示它们都是求和的意思。怎么样,理解透了吧,还有一定我们需要注意,我们得根据数据类型、参数的数量已经类型顺序的不同来进行方法重载。

封装

封装是面向对象的三大特性的一种,另外两种是多态和继承。那封装干啥的呢?它其实是为了避免逻辑错误,为什么这么说呢?通常我们创建了一个类,然后另一个人会用到我们的类,他们往我们类里的属性赋值,然而他可能会随便赋一些乱七八糟的值,就很有可能会犯逻辑错误,那我们就可以不给他访问我们属性的机会,我们把这个属性封装起来就行了。这里用到另一个修饰符private。表示私有的意思,表示这个属性不能在其他类里访问,只能在自身类里进行访问。到了这,我们是不是还有这样的疑问?封装固然可以避免错误,但是那不就没人可以给我们属性赋值了吗,那我们要这个属性的意义何在?不要急,我们可以创建setter来进行赋值,同时也可以创建getter方法来进行获取值。如果要避免错误,我们可以在方法里写一些赋值的一些约束代码。在进行方法赋值时,为了见名知意,我们尽量得让setter()方法里的形式参数列表的参数名尽量和成员属性名一样。但这样出现了相同的名称,如何判断谁给谁赋值呢?为了区分,我们这里需要用到this指针,这个this代表当前对象,所以成员属性名可以用this.成员属性名代替。

setter、getter以及this的代码示范

在这里插入代码片
class Employee{
String name;          //员工姓名
String tel;           //员工号码
double salary;        //员工基本薪资
double rate;          //员工薪水增长率

//构造类型
Employee() {

}

//setter、getter
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}

void setTel(String tel) {
this.tel = tel;
}
String getTel() {
return tel;
}

void setSalary(double salary) {
this.salary = salary;
}
double getSalary() {
return salary;
}

void setRate(double rate) {
this.rate = rate;
}
double getRate() {
return rate;
}

double getTotal() {
return salary+salary*rate;
}
}

静态

我们现在来看静态,怎么用到静态呢?假如你要描述团队成员,需要创建一个类,类里有姓名、年龄、班级、学科等属性。但是我们得知这个团队所有成员的班级、学科都一样,难道我们要一遍一遍的给同样的属性赋重复的值吗?难道就没有一次性可以解决的操作吗?有的,它就是静态,用静态定义这些相同值的属性,
格式: 类名.成员属性 = 内容;
其实静态你可以把它理解为类自己可以描述的特征,而这个特征为这个类下的所有对象所共有。简单理解就是静态属性赋的值,那么这个类下的所有对象静态属性值都是那个赋的值。同时静态不仅可以用在属性,也可以用在方法里。调用方法也一样,不要要用对象名。
格式是 类名.方法名();
那么这个方法执行的操作就相当于为这个类下的所有对象执行了一遍这个方法。

静态变量、静态方法的代码示范

在这里插入代码片
class Student{
private String name;    //姓名
private int age;        //年龄
private char sex;       //性别
private String hobby;   //兴趣
static String company;  //公司
static String subject;  //学科
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}

void introduce() {
System.out.println("姓名:"+name+",年龄:"+age+",性别:"+sex+",兴趣:"+hobby+",公司:"+company+",学科:"+subject);
}
}
public class StudentTest {
public static void main(String[] args) {
Student.company = "阿里巴巴";
Student.subject = "Java";
Student stu1 = new Student();
stu1.setName("张三");
stu1.setAge(20);
stu1.setSex('男');
stu1.setHobby("篮球");
stu1.introduce();
}
}

什么是包,可能我们现在感受不到包的重要性。但是当我们有一天做大项目时。每天面对成千上万代码时,这些代码当然不可能只存在一个类文件中,可能需要放在数百个类里。那我们把这些类进行一个划分存放。而包是用来存放划分的。可以把包理解为文件夹。方便整理。通常我们也会插入包,因为包里的类我们需要用到,比如Scanner类用来输入,String类用来表示字符串,不过字符串是系统默认插入了,在插入的时候,我们需要考虑这个类的权限修饰符。总共有4个。
自身类 自身包 子类 其他包
public √ √ √ √
protected √ √ √
default √ √
private √
√表示能访问。 一般只会用private进行封装数据,还有就是经常用public是为了方便访问。而其他修饰符很少用。所以在插入包的时候,能用的类只有public 这个修饰符的类。

代码块

代码块有普通代码块
格式为: { }
构造代码块
格式为: 类名(){ }
静态代码块
格式为:static { }
静态代码块只执行一次,也是最开始执行,因为它在类加载的时候就已经执行了。然后是普通代码块执行,执行次数根据对象的创建次数,可以看出它是在创建对象时执行。最后才是构造代码块的执行。当然我们经常都以为构造方法最先执行,那可能是我们没怎么用到静态代码块和普通代码块。虽然用得少,但还是要记住,说不定某天就用到了。

代码块的代码示范

在这里插入代码片
class Clothes{
//普通代码块
{
number++;              //每次创建对象时就开始递增1
}
//静态代码块
static {
System.out.println("执行中...");
}
//构造代码块
Clothes() {
super();
}
private static int number;     //序号
private String brand;          //品牌
private String type;           //类型
private int price;             //价格
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
void show() {
System.out.println("衣服序号:"+number+",衣服品牌:"+brand+",衣服类型:"+type+",衣服价格:"+price);
}
}

public class Demo {
public static void main(String[] args) {
Clothes clo1 = new Clothes();
clo1.setBrand("HLA");
clo1.setType("夹克");
clo1.setPrice(200);
clo1.show();
}
}

继承、多态

我们在上面是不是也提到了继承,它是面向对象的三大特性之一,属于类与类之间的关系。那什么是继承呢?其实就像儿子继承父亲的家产一样,就好比说儿子可以用爸爸的钱,那我们的类其实是一样的,继承的关系就是子类与父类的关系,子类可以用父类的方法。
继承格式:
class 子类名 extends 父类名 {
}
当然儿子也可以有骨气点,不啃老,自己有能力赚更多的钱,那子类也是一样的,它也可以有骨气点,自己可以重写父类那儿继承的方法,对,这就引入了重写概念,它和重载可别弄混淆。重写是指继承的子类改写父类的方法,而重载就是在自己类重新再加一个相同方法名的方法而已。重载和重写都属于多态,而这又是面向对象的三大特性之一。多态最主要还是对象的多种表现形态。

super、this

this都知道吧,它是表示当前对象,在前面我已经介绍过。那我们直接来看super(),这个是什么呢?为什么每次它都在我创建的构造方法里,其实它是父类构造方法,之所以你每次都看见它,是因为你这个类有继承。很多人看到这儿懵了,因为我们都在知道明明没继承类啊,我都没写继承,其实所有类都有继承,而这个继承的类是所有类的根,它只是隐藏罢了。

接口和抽象类

现在我们来学习一下抽象类和接口。那么抽象类是啥呢?可以简单理解为很抽象的一张图纸,这张图纸它可能只是稍微的设计了一下,但是并不全面,因为抽象类不提供具体功能的方法。它或许只提供部分。那你觉得这样的图纸可以做出我们想要的东西吗?答案当然是不可以,所以抽象类是没办法创建对象。现在我来写一下抽象类的创建方式:
abstract class 类名{
public void 方法名();
public void 方法名(){
} }
看见没有,方法下是没有语句块描述的。它不提供任何指令。
那么接口呢?接口其实比抽象类更抽象,因为它完全不提供实现或操作的语句指令。但是接口有一大优势。重点来了!请记好笔记。
类只能单继承一个类,就是只能继承一个类。很多人可能会想到Object类难道不是在默认继承吗?对,没错,可是你要清楚,被你继承的父类,或者父类的父类已经帮你继承好Object了。因为我们说过,Object是所有类的根,对吧。那么你现在和Object类就是多重继承关系,简单理解就是Object或许是爷爷,或者是隔着几代的祖宗。
那我们现在来看接口,它比继承就有优势了。因为类可以实现多个接口,但是类或者抽象类只能继承,而继承只能继承一个,所有这就是接口的优势。
接口的创建格式:
interface 接口名称{ 全局常量; 抽象方法;}
实现格式:
class 类名 implement 接口 {
}
怎么样,抽象类和接口清楚了吧。

equals()和toString()

我们平常判断两个值是否相等,一般都用"",对吧。但我们要清楚 “”只适用于基本数据类型的数值判断,它并不适用于判断字符串以及其他引用类型。为什么呢?因为它是判断栈内的地址值是否相同,它并不判断内容是否一致。而我们的基本数据类型的值都是存放在栈内的。但是当我们要判断引用类型,我们就得注意,它们的内容是存放在堆里的,而它们产生的地址是存放在栈内的,所以就算它们内容一致,它们产生的地址值是不一样的。
那怎么办呢?有什么方法判断内容吗?有,我们就重写一个equals方法就好,而这个方法是从Object那继承过来的。那我们怎么进行一个重写呢?我现在给大家讲一下这个思路
首先我们可以先判断这两个对象地址是否相同,如果地址相同了,那肯定就是同一个,因为不可能产生两个相同的地址。然后我们再判断这两个是否有一个为null(空),有为空的,肯定也不用比了。之后我们再判断类型是否相同,判断格式如下:
if(对象 instanceof 类){
}
看到这个instanceof了吗?它就是用来判断两个引用类型的对象是否是一个类。如果不是一个类,那也不用比了,如果是一个类,那我们就可以进行判断了,判断成员属性是否相同,如果是基本数据类型就可以用""来进行判断,如果是字符串类型我们用object的equals()方法进行判断,因为object提供的equals()方法至少是可以判断字符串内容是否相同的。如果判断符合,那我们就可以断定这两个引用类型的对象是相等了。否则就是不相等。
equals()格式如下:
变量名.equals(变量名)
对象名.属性名.equals(对象名.属性名)
对象名.getter().equals(对象名.getter())
最后我们来说一下toString()方法,我们每次输出字符串的时候,字符串变量有个方法是被隐藏了,它就是toString()方法,通常,toString()方法返回一个文本表示对象的字符串,结果应该是简洁但信息丰富的表示,便于人们阅读,建议所有子类重写此方法。
如果你直接用对象的toString()方法返回一个字符串,那返回的就是这个类的路径名@加上一些十六进制的乱码。所以我们有必要在重写一个toString()方法,用来返回我们想要的结果。
格式为:
对象名.toSting();
怎么样,学会了吗?

equals()方法的一种常用方式:判断是否登录成功
在这里插入代码片
public void login() {
if(username.equals("admin")&&password.equals("123")) {
System.out.println("登录成功...");
return;
}
System.out.println("用户号或密码输入错误...");
}

内部类

很多面试官问这个问题的时候,很多不知道内部类的人通常会听为累不累,哎,于是就被无情的pass掉了。所以我们还是有必要好好弄懂一下内部类,内部类,我们从它的字面意思也可以大概猜到它是类里面的类。可以这么说,没错。它总共分为四个:

成员内部类

定义格式如下:
class A{
class B{ }
}
调用格式:
A.B b = new A.new B();

局部内部类

定义格式如下:
class A{
public void a(){
class B{ }
调用格式:
B b = new B();
b.方法名();
}
}

在其他类的调用格式
A aa = new A();
aa.a();

匿名内部类

定义格式如下:
new 构造方法(){
类体
}

静态内部类

定义格式如下:
class A{
static class B{
}
}

调用格式:
A.B a = new A().B();

包装类

现在来看包装类,它的意思就是把不是对象的8种基本数据类型包装成对象。因为Java语言是一种面向对象的语言,而8种基本类型却不是对象,于是为了实现一切皆对象的基准,就有了包装。那我们现在来看看分别包装成什么对象。
int ----------Integer
char---------Chacter
float---------Float
double------Double
boolean----Boolean
byte---------Byte
short--------Short
long---------Long

怎么样,有发现规律吧,除了int和char,其他数据类型是不是就把首字母大写了一下,就没什么变化了。
其实我们不仅可以把基本类型包装成类,也就是把基本类型转换成对象,这个步骤叫做装箱。我们也可以把对象转换成基本数据类型,这一步叫拆箱,很好理解吧。现在我们简单了解一下装箱和拆箱

装箱和拆箱

装箱和拆箱都可以分为手动和自动,我现在分别把它们的操作写下来,以int类型为例:

手动装箱:
Integer i = new Integer(整型值);
自动装箱:
Integer j = 整型值;
手动拆箱:
int a = i.intValue();
自动拆箱:
int b = j;

好了,根据我前面说的话去理解,多敲下这些代码,你就会理解。

可变参数

现在我们来看可变参数,什么是可变参数?它是干嘛用的?比如说我们想实现n个数的和,要获取它的值,那如何去实现呢?我们肯定会认为这怎么可以?n都不知道,我们怎么获取。怎么创建方法。可是用户的需求就是要我们实现这样的功能,他想求几个数就求几个数,为了解决这个问题,我们就可以用到可变参数。因为我们不知道n是多少,不知道用户想要求几个数?那我们就创建一个可变参数,这样就可以实现用户得需求了。
方法名如下:
public 返回值类型 方法名(数据类型…变量名){
}
这个可变参数就是括号内得 数据类型…变量名。其实可变参数可以是一个一维数组,因为只有数值才能存储多个参数得值呀,对吧。同时,到这里我们需要注意一点,如果还有其他类型得值也要调用过来,那这个值一定是要放在可变参数的前面,因为我们不知道可变参数是多少?这样我们就可能出现异常,程序就会崩溃。

异常

异常就是我们经常说出现的bug,所以很多人被这个折磨的要死。那我们现在就好好学习一下什么是异常,我相信当我们对他有一个清晰的理解后,我们就能轻易的避开它。通常我们做完了一个项目,运行了一下,感觉效果良好,可是不知道为什么别人去运行就出现了很多bug呢?这是因为我们自己知道写的代码是什么,我们知道程序是如何操作的,也知道如何配合这个程序输入什么,可是我们得站在用户得身上去想,用户是不会像我们一样了解这个程序,他可能想输什么就输什么,想怎么操作就怎么操作,当用户碰到了我们一些没有考虑的点后,就很有可能出现bug。出现bug后程序也就崩溃了,不会执行了。
那如果程序崩溃了我们想继续往下执行,怎么办?我们就可以抛出异常,
具体格式如下:
public 返回值类型 方法名() throws 异常类名{
}
在这里我说一下bug它分为 Error(错误),通常表示Jvm出现的错误操作,只能尽量避免,无法用代码处理。那异常呢指的是Exception , Exception类,在Java中存在一个父类Throwable(可能的抛出)。这个父类下也包括Error类。
那么出现异常我们是可以避免的,我们可以抛出异常,也可以捕获异常。
怎么捕获呢?用 try{ }catch(异常类名 对象){ }
其实这个语句和if语句很相似,没有异常就执行try{}里的语句块,有异常就执行catch{}里的语句块。很多人捕获常似乎就提示一下用户出现了异常,然后哈哈一笑就没了。那程序还是停止了呀。所有我们有必要进行补救,异常出现时我们应提示用户该怎么操作,然后再继续执行程序,这样用户才会满意,对吧。

其实补救挺简单,只要我们发现了是由于什么原因出现的异常,我们只要提示用户该怎么去执行操作,然后再让程序继续执行,我们调用一下这个方法即可。

最后我们来看finally{},这个其实是写在try{}catch{}后面的,你可以用,也可以不用。它的功能是不管执行的是try{}里的代码块,还是catch{}里的代码块,它的代码块都必然要执行,那怕在前面已经执行过return;语句,这个一般代表方法的结束语句。就是说尽管结束方法,但是我们的finally{}的代码依然会执行,除非在前面执行了退出程序语句,只有结束程序,finally{}里的语句块才执行不了。这个一般是面试官会问到的问题哦,大家做好笔记。

结语

学习Java的路上并不枯燥,它会让我们越来越有成就感。请看到这里的你关注我,我将会保持每周一更的,让你让他让我们对Java不再困惑。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: