------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、什么叫面向对象
面向过程:
main()
{
开门();
进房间();
关门();
打开电视();
看电视();
}
这就是用面向过程的思想来描述这个问题,我们观察能发现其实面向过程解决问题就是把问题分成一个一个的小问题,然后利用函数进行模块化编程,来解决问题。
面向对象:
main()
{
门.开();
人.行走(房间);
门.关();
电视机.开机();
人.观看();
}
我们可以看到这就是使用面向对象的思路来解决同样的一个问题,似乎两种方式有很大的差别对吧?那么我们下面就分析一下它们的区别。
面向过程和面向对象的区别:
面向对象开发流程:
查找对象,建立对象,使用对象,维护对象之间的关系。
举例:我们要造汽车。首先我们需要一个图纸(汽车类),然后通过这个图纸造出不同的汽车(不同的对象),之后我们就可以使用汽车对象的功能了。
小结:
面向对象开发是一种开发思想,这种开发思想是基于面向过程的,它的核心是将万事万物看做对象(万物皆对象)。然后设计它们对应的类,然后根据每个对象的特点创建类的实例,之后就可以使用这些对象的功能了,这种开发方式在开发大型程序时的优势要远大于面向过程,它在代码的复用性,扩展性和可维护性方面都优于面向过程,因此现在的主流语言都是面向对象的,或者至少是部分面向对象。
二、什么叫做类
在上一小节中,我们提到了类,那么什么叫做类呢?这要从对象说起,所谓对象就是现实世界中一个个有形或者无形的具体事物,例如:桌子A,椅子B,人C,蚂蚁D,文章E等等。也就是说对象指的是一个具体的事物,比如一个叫做何龙的人,一条叫旺财的狗等等。到此我们发现问题了,我们总不能为每个对象都定义一吧,这样的话工作量也太大了点,光是人就要定义个几十亿个。。。。。因此我们发现虽然每个人都是特有的,但是每个人都有共性的东西,例如:姓名,年龄,身高,职业等等。我们是不是可以把这些共性的部分提取出来呢,然后在创建对象时,按照每个人对象的特性创建,答案是可以的,这就是类。类就是对一类事物的描述,例如学生类,它包含有姓名,年龄,成绩,学号等,但是它又不会具体的说明姓名是什么,或者学号是什么等,而是在创建对象的时候由创建对象来决定这些属性的具体数值。
类的作用:
类的成分:
属性--成员变量:
说到这里,大家都能看出成员变量与局部变量的不同了吧。那么成员变量具体是干什么的呢?
通俗的说它就是形容一个事物的特征的。例如形容人:名字,年龄,身高,职业等。这都是形容一个人的特征,因此它们都属于成员变量。
方法--成员方法:
属性,方法与类:
三、四种引用类型
什么是引用类型呢?
引用类型分类:
a.强引用:A a = new A();此时即便内存不够了,虚拟机也不会释放该内存而是抛出异常终止程序。
b.软引用:内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存,
软引用可用来实现内存敏感的高速缓存。
c.弱引用:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,
一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
d.虚引用:虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,
在任何时候都可能被垃圾回收器回收。
四、面向对象三大特征之封装
面向对象编程本身就是在不停的封装,封装事物,形成类,这就好像我们使用电脑一样,电脑就是被封装好了,内部的结构,硬件都隐藏起来了,对外提供的就是主机上的几个键和一堆接口而已。这样封装的目的也很明显,首先我们使用电脑更简单了,看起来也舒服啊对吧。其次就是更安全了,因为我们本身不懂电脑的内部结构,如果就这么暴露出来,那很容易不小心破坏它,而且由于我们不懂,所以出了问题也没法解决。再其次就是我们根本没必要看到内部的结构,我们只是使用电脑的功能而已。
这就是封装的好处,可以让我们在轻松简单的使用电脑强大的功能的同时,不需要去了解电脑的内部结构,而且也保护了电脑本身不会轻易的被破坏。
五、构造函数
特点:
作用:
与成员方法区别:
除了上述的特点的区别外,在使用上也有区别,成员方法作为类的功能,是在被对象或类(静态方法)调用时才执行,而构造函数则是在对象创建是自动被系统调用执行的。成员方法可以被调用多次,这就好像一个人可以多次吃饭一样,而构造函数只在创建时调用一次,这就好像人只在出生的时候起一次名字,确定一次性别一样(比喻不太恰当哈)。
定义构造函数:
class Demo
{}
我们发现其中并没有构造函数,难道构造函数也像成员函数一样是可有可无的吗?
class Demo
{
Demo()
{}
}
那么我们就要考虑了,到底什么时候需要自己定义一个构造函数呢?
class Person
{
private String name;
private int age;
private double weight;
Person(String name,int age,double weight)
{
this.name=name;
this.age=age;
this.weight=weight;
}
}
我们可以看到,这个人有三个属性,也就是
成员变量:name,age,weight。它有一个
构造函数Person,当jvm发现这个构造函数后就不会自动添加那个无参构造函数进来了。在构造函数内,我们使用三个参数分别对三个属性进行了初始化,这里我们发现了一个特别的词this,这个我们下节讨论。
class Person
{
private String name;
private int age;
Person()
{
name=null;
age=0;
}
Person(String name)
{
this.name=name;
}
Person(String name,int age)
{
this.name=name;
this.age=age;
}
}
可以看到,构造函数也是可以重载的,且重载的原则与普通函数一致。
构造代码块:
说到构造函数,不得不提 另一种初始化方式,那就是构造代码块。构造代码块的功能也是对对象进行初始化,不过不同于构造函数的是, 构造代码块是对该类创建的所有对象进行初始化,而构造函数是有针对性的进行初始化。class Chinese
{
private String name;
private int age;
private String Country;
Chinese(String name,int age)
{
this.name=name;
this.age=age;
}
{
Country="中国(CN)";
}
}
class PersonDemo
{
public static void main(String[] args)
{
Chinese ch1=new Chinese("helong1",22);
Chinese ch2=new Chinese("helong2",29);
}
}
我们可以看到,我们
使用构造函数对姓名和年龄这个每个对象都不一定相同的属性进行初始化,而使
用构造代码块对中国人类的每个对象的国籍进行初始化,因为我们知道中国人类的国籍都是中国,这是大家都一样的部分,因此我们使用构造代码块来进行初始化。
六、this关键字
what is this?
class Demo
{
private int count;
Demo(int c)
{
count=c;
}
public void show()
{
System.out.println(count);
}
}
main方法中:
{
Demo d1=new Demo(22);
Demo d2=new Demo(33);
d1.show();
d2.show();
}
我们知道打印结果是22和33,那么我们不仅要问了,我们都知道,类中的方法是存在方法区的,且只有一份,也就是对象共享的,而且方法内并没有标示是打印哪个对象的count属性,那么jvm是如何确定的呢?
this作用:
class Demo
{
private int count;
private int temp;
Demo(int count)
{
//1.解决局部变量和成员变量同名的情况
this.count=count;
}
Demo(int count,int temp)
{
//2.用于构造函数间互相调用
this(count);
this.temp=temp;
}
}
下面我们解释一下这两个作用:
Demo(int count)
{
count=count;
}
我们的初衷是将count赋给对象的count,但是编译器可不知道,编译器查找一个名字的顺序是:
先从局部变量开始找(参数也是局部变量),找不到再去找成员变量等,因此此处在编译器看来就是将参数count的值赋给了参数count。这明显不是我们的目的,因此我们需要
使用this来表示第一个count是当前对象的count属性,如下代码:
Demo(int count)
{
this.count=count;
}
这样就达到了我们的目的,且很好的
保持了命名的规范性和代码可读性。
2.当我们在使用构造函数初始化对象时,经常会重载构造函数,例如人类的构造函数,我们可以定义一个初始化姓名的,再来一个初始化姓名和年龄的,这个时候在定义初始化姓名和年龄的构造函数时,如果可以把对姓名的初始化交给那个初始化姓名的构造函数来做,那是大大的提高了代码的复用性的,因此我们需要 构造函数间的互相调用。而 构造函数的互相调用不像其他成员方法那样直接使用方法名,传入参数即可。而是用this来代替方法名,传入参数。因此可以说 this的这个作用的主要目的是提高代码复用性。
七、static关键字
static简介:
静态特点:
静态成员:
静态利弊:
静态代码块:
到此我们学了很多初始化的方式了,包括:构造函数,构造代码块,静态代码块,堆中的默认初始化,类中的显式初始化,那么当它们都存在时的顺序是怎样的呢?
class Demo
{
private int count=5;//类中显式初始化
Demo(int count)
{
//1.解决局部变量和成员变量同名的情况
System.out.println("构造函数初始化前的count:"+count);
this.count=count;
System.out.println("这是构造函数!");
}
{
System.out.println("构造代码块初始化前的count:"+count);
count=6;
System.out.println("这是构造代码块!");
}
static
{
System.out.println("这是静态代码块!");
}
public void show()
{
System.out.println(count);
}
}
class DemoTest
{
public static void main(String[] args)
{
Demo d=new Demo(10);
d.show();
}
}
运行图:
从图中可以得到结论: 最先运行的是静态代码块,其次是显式初始化,然后是构造代码块,之后是构造函数。
八、综合练习
/*
4.面向对象:查找对象,建立对象,使用对象,维护对象间的关系
5.匿名对象(只使用一次对象的方法时)
6.成员变量和局部变量
7.封装(函数是最小封装体)练习基本封装思路(所有数据封装,提供公共访问接口set,get)
8.构造函数(重载),构造代码块(运行优先于构造函数)
9.this关键字
a.局部变量和成员变量同名的问题
b.构造函数间互相调用(此语句需放到第一行最先执行---即:当初始化函数中还有初始化语句时,要最先执行初始化语句)
*/
class Person
{
private String name;
Person()
{
name="";
age=0;
}
Person(String name)
{
if(name.length()>20)
System.out.println("名称过长,请少于20个字符!");
else
//通过this确定成员变量,jvm查找name时优先在函数内部找,如果不加this,则两个name都是指形参
this.name=name;
}
Person(String name,int age)
{
//this用法:构造函数间互相调用,必须放在函数第一行
this(name);
if(age<=0||age>130)
System.out.println("年龄数据不正常!");
else
this.age=age;
}
public void setName(String name)
{
if(name.length()>20)
System.out.println("名称过长,请少于20个字符!");
else
//通过this确定成员变量,jvm查找name时优先在函数内部找,如果不加this,则两个name都是指形参
this.name=name;
}
public String getName()
{
return this.name;
}
private int age;
public void setAge(int age)
{
if(age<=0||age>130)
System.out.println("年龄数据不正常!");
else
this.age=age;
}
public int getAge()
{
return this.age;
}
public void show()
{
System.out.println("姓名:"+this.name+",年龄:"+this.age);
}
}
class Test2
{
public static void main(String[] args)
{
//匿名对象
new Person().show();//姓名:,年龄:0
Person p1=new Person();
p1.setName("helong");
p1.setAge(23);
p1.show();//姓名:helong,年龄:23
Person p2=new Person("liudongyuan");
System.out.println(p2.getName());//liudongyuan
p2.setAge(144);//年龄数据不正常
p2.show();//姓名:liudongyuan,年龄:0
Person p3=new Person("zhegemingzizhenshichangashibushibuhefaa",111);//名称过长,请少于20个字符
p3.show();//姓名:null,年龄:111
}
}
运行图:
九、设计模式--单例设计模式
什么是设计模式:
单例设计模式:
目的:保证类在内存中只存在一个对象。
作用:例如配置文件类的对象,我们知道不管哪个部分在使用或者修改该配置文件,本质上都应该修改的是同一个配置文件,因此可以应用单例设计模式。
实现单例设计模式的三步骤:
1.构造函数私有化(使得外界不能通过类创建对象)。
2.类中定义一个私有静态本类对象成员(作为那个唯一的一个本类对象存在,私有是保护它不能被外界直接访问,静态是为了被静态方法访问)。
3.定义一个公有静态方法返回唯一对象(公有是为了外界都能使用,静态是为了类名调用)。
要实现单例设计模式有两种方式:懒汉式,饿汉式。
饿汉式:
class SingleClass
{
private String name;
private static SingleClass sc=new SingleClass();
private SingleClass()
{
name="helong";
}
public static SingleClass getInstance()
{
return sc;
}
public void setName(String str)
{
name=str;
}
public void show()
{
System.out.println(name);
}
}
class SingleClassTest
{
public static void main(String[] args)
{
SingleClass s1=SingleClass.getInstance();
s1.show();
s1.setName("123123");
SingleClass s2=SingleClass.getInstance();
s2.show();
}
}
饿汉式:
class SingleClass
{
private String name;
private static SingleClass sc=null;
private SingleClass()
{
name="helong";
}
public static SingleClass getInstance()
{
if(sc==null)
sc=new SingleClass();
return sc;
}
public void setName(String str)
{
name=str;
}
public void show()
{
System.out.println(name);
}
}
class SingleClassTest
{
public static void main(String[] args)
{
SingleClass s1=SingleClass.getInstance();
s1.show();
s1.setName("123123");
SingleClass s2=SingleClass.getInstance();
s2.show();
}
}
public static SingleClass getInstance()
{
if(sc==null)//A
sc=new SingleClass();//B
return sc;
}
中的A处是判断成功了,因此此时还没有初始化sc对象,但是线程1失去了执行权,由线程2来执行,线程2也执行到A处时,依然判断成功,因为还是没有对sc进行初始化,因此在线程2中,为sc赋值了一个对象,并返回了。此时线程2失去了执行权,由线程1继续从B处执行,也就是又创建了一个对象并返回,因此不符合我们单例设计模式的初衷,
内存中出现了多个对象。
public static synchronized SingleClass getInstance()
{
if(sc==null)
sc=new SingleClass();
return sc;
}
使用synchronized来标示SingleClass方法,使得该方法在被一个线程操作时,其他线程不能操作,但是我们发现这种做法的效率极低,因为我们其实只是需要在sc为null时,进行措施而已,而不是每时每刻都进行这种同步处理,因此我们一般使用下面这种效率更高的方式:
public staticSingleClass getInstance()
{
if(sc==null)
synchronized(SingleClass.class)
{
if(sc=null)
sc=new SingleClass();
}
return sc;
}
可以看出这种方式的效率高于上一种。
开发中一般使用饿汉式,因为相对来讲,效率方面与懒汉式没什么区别,但是饿汉式的代码更简洁。
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------