oop上半部分:
(希望同学们在下面的课程里充满想象力,我会用生活的例子教你们)
(1)问题的提出:
想像一下你的邻居养了条狗,这狗可能是条藏獒,是条柴犬、土狗无所谓。这个老太婆知道你学过C语言,叫你用程序记录下来。
想像一个老大妈的狗需要你编写一个程序记录这条狗的生活(吃、喝、拉、撒)。如果现在是你,你现在拿C语言怎么写?需求是必须要有这条狗的名字,种类。比如这条狗叫Tom,这狗品种叫藏獒,年龄两岁。其实感觉就是一张表格,就好比给狗填一张表格。那就用C语言伪代码写一下。
#include<stdio.h>
void eat(){
printf("Tom在吃饭.....");
}
void sleep()
{
printf("趴着睡,躺着睡");
}
//生病啊,啥的
int main(void)
{
char *name =Tom;
int age =2;
char *variety ="藏獒";
return 0;
}
还有很多的东西,然后这个老太婆觉得你这个系统很不错,决定包养你,偶不然后她给你钱了。你发现这是商机啊!你就去问王大爷这套系统要不要,张大爷说今后你就是他的得力干将。然后你打开笔记本发现又要再写一个程序,你又稀里糊涂写了一个程序。你就写了两个程序。那这个问题有什么方法解决吗?
(2)烦人:
你可能想到了,用函数传参。
#include<stdio.h>
char *name;
int age;
char *variety;
void eat(){
printf("Tom在吃饭.....");
}
void sleep()
{
printf("趴着睡,躺着睡");
}
//生病啊,啥的
int main(void)
{
dog("Tom",2,"藏獒”);
dog("Jerry",2,"拉布拉多”;
eat();
sleep();
return 0;
}
void dog(char *n,int a,char *v){
*name =n;
age =a;
*variety =v;
}
你会发现这种方案好烦, 传参啥的,然后张大爷把你的传奇故事给你小区的大爷大妈说了,假如有100多号人你怎么写?即便用结构体,传参太烦人了,你该怎么办呢?从main函数自上到下依次执行,100多个人找你写程序,会不会觉得烦人。
(2)变换思维
你发现没有我们现在的思维是这狗的名字,这狗的品种,这狗吃饭,这狗睡觉,感觉就像一条直线,先干嘛,再干嘛。全程都有一个词过程
1、过程
1)思考一些过程,
2)思考第一步怎么做
3)思考第二步怎么做
4)接着怎么做
5.....
6)最后怎么做(return 0);
这狗的名字就定义一个字符串,这狗吃饭就定义一个函数。想到的时候再去做,当你饿的时候才想到翻冰箱,当你想睡觉的时候,才上床盖被子睡觉。
你想去做什么的时候去做了,然后去做了,这就是过程。
那现在我们改变一下思维,我们不能把张大爷的狗每天吃饭的过程都写一遍,也不可能把老太婆每天的经历写一遍。两条狗的过程是不一样的。所以我们不能用过程去写。
走一步看一步:没有目标没有理想的咸鱼。
基于过程缺点:目标不明确,不适用大众(你朋友走一步看一步,可能在打工,你走一步看一步,可能就在打工)。
所以这个程序要完成这些目标:
1、该程序,要大众化。(你的狗有名字,我的狗应该也有名字)
2、目标。(有了目标,就知道你下一步就能干啥)(给100多条狗写程序)
3、不强调过程。(不惜一切代价,什么事都能干)(不强调狗怎么睡觉)
目的:记录下这条狗24小时干的事,我们干事强调目标,不强调过程。
pop:面向过程(C语言)(目标不明确)(追踪目标太复杂)
oop:面向对象(对象就是目标)(张大爷家的狗24小时做的事)
(3)规划明确目标站在更高层次解决问题
明确目标:比如你要考研,
规划、计划:你要设计它,第一个月要学啥,第二个月要学啥。当你把这些计划、规划执行完的时候,
就完成了目标。
那现在设计狗的系统,你也要设计好,你不可能一个个问你的邻居,说白了面向对象编程,就是在更高的层次看待事物:这狗可能越来越多,大爷大妈可能越来越多,你还需要关心这狗几点睡觉,几点吃饭。而应该是用户去记录他的狗几点吃饭,几点睡觉。那你就站在更高的层次看待事物了。
(4)上代码,设计体验面向对象编程,实例和对象
设计思维:
去考虑
先考虑共性。(名字、类别、年龄)
所有的狗都会的东西
公共的特性谁家的狗都这样,设计完成,目标完成
idea快捷键:shift+f6改名,alt+insert:快速创建
package com.xxxx.bean
public class Dogs{
//故意没写main方法,所有的狗都有名字,都有类别,都有年龄。
public String name;
public String variety;
public int age;
//所有的狗都会睡
void eat(){
System.out.println(name+"狗吃了啥");
System.out.println(this.name+"狗吃了啥");
}
void sleep(){
System.out.println(name+"狗睡觉");
}
void sick(){
System.out.println("狗生病");
}
}
//谁家的狗都满足上述目标,所有目标完成,设计也完成。
那现在这个项目写完了,再写一个类表示程序运行。
public class Application{
public static void main(String[] args){
//张大爷!你可以试试了!这个App,先注册
Dogs zhangDog= new Dogs();
zhangDog.name ="Tom";
zhangDog.age =2;
zhangDog.variety="藏獒";
// 张大爷的狗睡觉了
zhangDog.sleep();
//王阿姨你也可以试试了,先注册
Dogs wangDog =new Dogs();
//发现没就分开了
wangDog.name= "Jerry";
wangDog.age =3;
wangDog.variety="拉布拉多";
}
}
从目标的角度去区分,这条狗所有的信息都归这个账号管,zhangDog是目标,wangDog也是目标,可操作的东西还是目标这东西叫啥:
我们有一个专业的名词叫做对象——或者叫实例。你想一下狗原本是抽象的,天下这么多的狗,我们创建了一个实例张大爷家的狗。
那对象和实例有什么区别?对象其实大于实例,实例:现实的一个东西对抽象的东西进行表达,是一个活生生存在的事物,它是唯一的。
一个活生生的事物对抽象的事物进行表达,那别人问你什么是狗,你一指张大爷家的狗就是。所以你可以叫对象也可以叫实例。
我们一直操作这个实例,这个对象。
那现在面向对象编程理解了吗?
(4)去你md成员变量行为类和this
类当中的变量和方法都总称为属性:共性和特性。
成员变量:它们组成和构成了类。(也属于属性)。
它们是类的组成部分。
一个动作,这些方法(函数)在类当中,我们有个 很好的名字——行为。
zhangDog.name:对象的属性。
this:指调用对象。
package com.xxxx.bean
//狗类 都具有这些属性(共性、特性)
//类当中的变量和方法都总称为属性(共性、特性)
// 假如现在李阿姨家的狗是二哈,会发疯。(这是特性)
public class Dogs{
//这些变量定义为成员,(成员的定义:社会团体、社会组织和家庭的组成成员)
//成员变量:他们组成和构成了类,所以我们这样命名
// 他们是类的组成部分,宠物狗没有这些就不能称为宠物狗
public String name;
public String variety;
public int age;
//一个动作,这些函数(方法)在类当中,我们有一个很好听的名字————行为
void eat(){
System.out.println(name+"狗吃了啥");
System.out.println(this.name+"狗吃了啥");
//所以this就指代这个对象zhangDog
}
void sleep(){
System.out.println(name+"狗睡觉");
}
void sick(){
System.out.println("狗生病");
}
}
public class Application{
public static void main(String[] args){
//张大爷!你可以试试了!这个App,先注册
Dogs zhangDog= new Dogs();
zhangDog.name ="Tom";
zhangDog.age =2;
zhangDog.variety="藏獒";
// 张大爷的狗睡觉了
zhangDog.sleep();
//王阿姨你也可以试试了,先注册
Dogs wangDog =new Dogs();
//发现没就分开了
wangDog.name= "Jerry";
wangDog.age =3;
wangDog.variety="拉布拉多";
}
}
(5)注销账户和空指针异常
张大爷突然觉得你这个系统不好用,那你只能让他注销吧
//账户注销吧
zhangDog = null;
//然后你再输入
System.out.println("zhangdog "= +zhangDog.name);
出现NullPointerException:空指针异常。
说明对象出现问题。那这系统里就不会有zhangDog的属性了。
(6)oop封装:
为什么张大爷觉得你的系统不好用呢?
肯定是你系统做的有问题。
张大爷说你系统不安全,说王阿姨就能把你家狗信息改掉。
比如 zhangDog.age =-100;张大爷多按了一个符号,那怎么办呢?加个if?那就不符合面向对象了。
package com.xxxx.bean
public class Dogs{
// 那我改成private
private String name;
private String variety;
private int age;
void eat(){
System.out.println(name+"狗吃了啥");
System.out.println(this.name+"狗吃了啥");
}
void sleep(){
System.out.println(name+"狗睡觉");
}
void sick(){
System.out.println("狗生病");
}
}
public class Application{
public static void main(String[] args){
Dogs zhangDog= new Dogs();
//比如我现在把名字改为,用户不能为所欲为(public就会造成)。所以我们改为private
zhangDog.name ="草泥马";
zhangDog.age =2;
zhangDog.variety="藏獒";
zhangDog.sleep();
Dogs wangDog =new Dogs();
wangDog.name= "Jerry";
wangDog.age =3;
wangDog.variety="拉布拉多";
}
}
我们把成员变量做的安全,使用private代替public成员变量的特性,我们另外提供getter and setter,这种方法叫做OOP-------封装。
比如:zhangDog=-100;
public:公共的,公有的——用户可以为所欲为。
private:私有的,用户不可以为所欲为。
我们要给用户一个能设置的,能获取的,但不能瞎搞的:
private int age;
public void setAge(int age){
if(age<0||age>100){
this.age=0;
}
else{
this.age=age;
}
}
//就能得到年龄。
public class Application{
public static void main(String[] args){
Dogs zhangDog= new Dogs();
zhangDog.setAge(20);
System.out.println(zhangDog.getAge());
}
}
所以我们用getter、setter方法和private一起用。
我们用alt+insert,选择geter和setter自动生成。
(7)引入jar包lombok(自动生成getter、setter)
在setting里找到plugins,搜索lombok,如果找不到请使用科学上网。
@Getter
@Setter
爆红了对吧!去引入jar包。
去maven repository:下载jar包,在idea下新建jar文件夹,拖进去,add as library。就导入了
如果还爆红了,出现问题,去看setting里的build中,找到Annotation Processors,设置Enable annotation processing。去build里rebuild project就可以使用了。如果还有问题去GitHub官网。
特殊的单写就可以了————方法的重写。
(8)To__string:自己玩(我现在输出一个对象和我不加toString输出一个对象
你用工具可以,但是getter,setter方法自己一定要会写。
lombok@Tostring;
@Tostring
System.out.println("zhangDog"+zhangDog);
(9)构造方法:
public class Application{
public static void main(String[] args){
//张大爷!你可以试试了!这个App,先注册
Dogs zhangDog= new Dogs();
//狗的信息的设置
zhangDog.name ="Tom";
zhangDog.age =2;
zhangDog.variety="藏獒";
}
}
但是实际应该就该初始化一些信息
初始化(定义加使用)
//初始化
int[] arr={0,1,2,3}
//先定义
int[] arr=new int[4];
arr[0]=1;
构造方法不要加类型:
//有参构造器
//alt +insert Constructor
public dogs(String name,Steing variety,int age,String food){
this.name=name;
this.variety=variety;
this.age=age;
this.food=food;
}
构造器的作用是为了初始化。
(10)构造方法的重载和再探this
this指对象。
//这不就是重载吗?
//重载函数名相同,变量名不同
public dogs(String name,Steing variety){
this.name=name;
this.variety=variety;
}
(11)垃圾回收机制:
比如:账号注销了:zhangdog=null;
其实并没有把它释放掉。
手动垃圾回收:system.gc();就能进行垃圾回收。
它其实是可以自动垃圾回收。
(12)静态变量和静态方法:建立在类上了,在变量名(或方法名前)加static。
程序火了,小区居委会找你,现在有100多个用户,你要有个标志确定这100多个人是这个小区的。小区名叫NanGua。
静态方法和静态变量:我可以用类名直接去用。
public class Dogs{
public static String plot ="NanGua";
}
//重点在static
public class Application{
public static void main(String[] args){
System.out.println("NanGua"+Dogs.plot);
}
}
//整个小区的狗都是NanGua小区的。
那如果我们整个小区的狗都要打针,整个小区的狗都要来。公开的,不是张大爷的特权,使用了static,我们使用了类名。
public class Dogs{
public static void injection(){
System.out.println("所有的狗都要打针")
}
}
public class Application{
public static void main(String[] args){
Dogs.injection();
}
}
(13)private static:
public class Dogs{
public static String plot ="NanGua";
}
使用public,可能有个安全问题,假如有个黑客,他改你代码
public class Application{
public static void main(String[] args){
Dogs.plot="Hack...";
}
}
于是:
public class Dogs{
private static String plot ="NanGua";
public static String getPlotInstance(){
return plot;
}
}
public class Application{
public static void main(String[] args){
System.out.println(" "+Dogs.getPlotInstance());
}
}
这样就只能获取了。
(14)内部类扯淡总结:
实际开发过程中,内部类并不常用。
public class Earth{
class Sun{
}
}
不推荐用内部类,除非为匿名内部类。
(15)static 单例模式 (单例设计模式,面试题)
static是建立在类上,你不可能建立在对象上,你不可能小区名还要问张大爷。更合理的是直接在程序里输出,而好处就是在这。
我们新建一个类sun。
public class Earth{
}
public class Application{
public static void main(String[] args){
Earth earth1 = new Earth();
Earth earth2 = new Earth();
}
}
//发现了吗?是不是不合理了,地球只有一个
static就能解决这个问题
public class Earth{
private static Earth instance =new Earth();
public Earth(){
}
public static Earth getInstance(){
return instance;
}
}
public class Application{
public static void main(String[] args){
Earth earthInstance = Earth.getInstance();
}
}
(16)OOP上半部分就结束了:
欢迎提问,但是提问之前请看《提问的智慧》。
我觉得这些文章的生动性,应该能把面向过程的思维引向面向对象。