在描述对象时候,例如描述对象为狗,可以设置Dog.age= 100;看上去语法没有任何毛病,但是显然这么写是不合理的。因此,java提供了封装的机制,将类和对象的成员变量进行封装。
案例1:
class FengZhuang
{
publicstatic void main(String[] args){
Dogjinmao = new Dog();
jinmao.setAge(180);
//jinmao.age= 100;
}
}
class Dog
{
private int age;
public void setAge(intage){
if(age > 20){
System.out.println("滚犊子!");
return;
}
System.out.println("设置成功");
this.age = age;
}
}
概念:
封装是面向对象三大特征之一,它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类提供的方法来实现对内部信息的访问。这样,当我们设置属性值的时候,可以判断设置的属性值是否合理。
//TODO
private int age;
publicvoid setAge(int age){
if(age> 20){
System.out.println("滚犊子!");
return;
}
System.out.println("设置成功");
this.age= age;
}
封装是面向对象编程语言对客观世界的模拟,在客观世界里,对象的信息都是被隐藏在对象内部,外界无法直接操作访问和修改。就如对Dog.age设置属性那样,外界不能直接修改age属性,只能随着岁月的流逝(条件),age才会增加。
封装的目的:
1、 隐藏类的实现细节。
隐藏和封装的区别:
隐藏就是将属性私有化(private),不能被外界直接访问。
封装比隐藏多一步,另外提供公有的方法让外界去操作该隐藏属性。
如下:temp是内部实现功能的细节,只需要隐藏。而age是描述对象属性,需要隐藏起来后提供公有方法对其进行访问和设置。
class Dog
{
//这个变量的业务逻辑只是在该对象方法中有一个记录
//临时数据功能
privateString temp;
privateint age;
publicvoid setAge(int age){
temp= "这个狗狗的年纪是"+age;
if(age> 20){
System.out.println("滚犊子!");
return;
}
System.out.println("设置成功");
this.age= age;
}
publicint getAge(){
System.out.println(temp);
returnage;
}
}
2、让使用者只能通过预定义的方法来访问数据,从而可以在该方法中添加控制逻辑,限制对成员变量的不合理的访问。
3、可进行数据检查,从而有利于保证对象完整性。
4、提高代码的可维护性。
为了实现良好的封装,需要从两个方面考虑:
1、将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。
2、将方法暴露出来,让方法来控制对这些成员变量进行安全访问和操作。
因此封装实际上就是:将该隐藏的隐藏,将该暴露的暴露。而实现这一目的则是通过java提供的访问控制符来实现。
访问控制符(权限修饰符)
java提供3个访问控制符关键字,private、protected、public分别代表三个访问控制级别,另外还有一个默认访问控制级别,一共4个访问控制级别。
默认访问控制即不写权限修饰符(default)。
控制级别由小到大排序为:
private->default->protected->public
使用4种权限类型控制的效果:
private:修饰的成员(变量、方法、构造器)只能在当前类内部访问,外界是无法获取该成员。显然,使用这个修饰符可以达到隐藏成员变量的目的。
class FengZhuang
{
public static void main(String[] args){
Dog jinmao = new Dog();
jinmao.age = 100;
}
}
class Dog
{
private int age;
}
default:默认修饰符,不适用任何修饰符关键字修饰。修饰的成员或类可以被相同包下的其他类访问,不能被其他包下的类访问。
案例:定义bao1. Bao1Class, bao1. Bao1Class2, bao2. Bao2Class,在bao2下的Bao2Class类无法访问在bao1下的Bao1Class的name属性。但是,bao1下的Bao1Class2类可以访问在bao1下的Bao1Class的name属性。
package bao1;
public class Bao1Class{
staticString name;
}
package bao2;
import bao1.*;
public class Bao2Class{
publicstatic void main(String[] args){
Bao1Class.name= "小明";
}
}
package bao1;
public class Bao1Class2{
publicstatic void main(String[] args){
Bao1Class.name= "小明";
}
}
protected:子类访问权限修饰符,修饰的成员可以被同包、不同包下的子类访问。通常使用该修饰符修饰的方法,希望其子类能重写这个方法。
package bao1;
public class Bao1Class{
staticprotected String name;
}
Bao2Class不可以访问Bao1Class的name属性
package bao2;
import bao1.*;
class Bao2Class {
publicstatic void main(String[] args){
Bao1Class.name= "小明";
}
}
package bao1;
public class Bao1Class{
staticprotected String name;
}
Bao2Class可以访问Bao1Class的name属性,因为有继承关系
package bao2;
import bao1.*;
class Bao2Class extends Bao1Class {
publicstatic void main(String[] args){
Bao1Class.name= "小明";
}
}
public:公开访问权限,这是最宽松的访问权限级别,修饰的成员、类,那么这个成员或者外部类可以被所有包下所有类访问。
权限最大,在任意位置,都可以访问(注意:导包)
修饰符修饰类:
注意:外部类只能用public 或者默认权限修饰,不能使用private、protected来修饰。
因为被这两个修饰的类不能被访问,也就没有任何意义。
//TODO代码验证权限修饰符
测试public修饰的类:
package bao1;
public class Bao1Class{
staticpublic String name;
}
package bao2;
import bao1.*;
class Bao2Class{
publicstatic void main(String[] args){
Bao1Class.name= "小明";
}
}
默认修饰符(default):
package bao1;
class Bao1Class{
staticpublic String name;
}
这样是无法访问的,因为Bao1Class权限仅限bao1包下,bao2中的类无法访问。
package bao2;
import bao1.*;
class Bao2Class extends Bao1Class{
publicstatic void main(String[] args){
Bao1Class.name= "小明";
}
}
这样是可以访问的,因为Bao1Class2和Bao1Class在一个包下。
package bao1;
class Bao1Class2{
publicstatic void main(String[] args){
Bao1Class.name= "小明";
}
}
不能使用private和protected修饰类
总结已学的修饰符。
static:静态修饰符;
private:私有权限修饰符;
default:默认权限修饰符;
protected:子类权限修饰符;
public:公开的权限修饰符。
当使用private修饰构造器的时候,会出现什么情况?
Private修饰的方法,作用范围是:类本身,其他类不能调用。
那么将对象的构造器方法都私有后,就无法创建对象了,那么在实际应用中,还可以通过暴露一个公开的方法,让外界获取一个对象。
classDuoTai
{
publicstatic void main(String[] args){
Personperson = Person.getPerson();
person.method();
}
}
classPerson
{
privatePerson(){
System.out.println("构造器方法执行");
}
publicstatic Person getPerson(){
returnnew Person();
}
public void method(){
System.out.println("执行person中的方法");
}
}
单例设计模式
这种案例在实际开发中,我们以单例模式举例。
当我们上网时候,发现顾客是多个,但是服务生是一个,并不是一个顾客有一个服务生。
那在这个时候,网管就是唯一的,那网管就是单例对象。
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
· 1、单例类只能有一个实例。
· 2、单例类必须自己创建自己的唯一实例。
· 3、单例类必须给所有其他对象提供这一实例。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的对象频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
更多相关资料请查阅:http://www.runoob.com/design-pattern/singleton-pattern.html
代码实现:
class Singleton
{
publicstatic void main(String[] args){
/*显然,这种情况不符合常理
实际中,一个网管,服务多个顾客,
网管就是唯一的,我们把网管对象变为唯一
对象,就符合常理了。
*/
Customerc1 = new Customer();
c1.surfing();
Customerc2 = new Customer();
c2.surfing();
Customerc3 = new Customer();
c2.surfing();
}
}
class Customer
{
publicvoid surfing(){
Waiterwaiter = Waiter.getWaiter();
waiter.kaiji();
}
}
class Waiter
{
privateWaiter(){
}
privatestatic Waiter waiter;
publicstatic Waiter getWaiter(){
//这个时候,我要保证每次获取网管对象都是唯一的
if(waiter== null){
waiter= new Waiter();
returnwaiter;
}
returnwaiter;
}
int temp =0;
publicvoid kaiji(){
System.out.println("开机" + temp +"号电脑");
temp++;
}
}
接下来介绍单例模式的两种实现方式:
1、 懒汉式,顾名思义,获取单例对象的操作采用偷懒的方式。
在代码中,那就是说:你什么时候需要用到该对象,那么什么时候再创建。
如下代码:当调用getWaiter方法获取对象时,才会创建对象。而不调用getWaiter方法的话,是不会创建对象的。
class Waiter
{
private Waiter(){
}
privatestatic Waiter waiter;
publicstatic Waiter getWaiter(){
//这个时候,我要保证每次获取网管对象都是唯一的
if(waiter== null){
waiter= new Waiter();
returnwaiter;
}
returnwaiter;
}
int temp =0;
publicvoid kaiji(){
System.out.println("开机" + temp +"号电脑");
temp++;
}
}
2、 饿汉式
和懒汉式相反,在类存在的时候,该类型对象就存在了!即便你没有调用getWaiter方法,该对象也存在!
class Waiter
{
privateWaiter(){ }
privatestatic Waiter waiter = new Waiter();
publicstatic Waiter getWaiter(){
/*这个时候,不管有没有调用该方法,
返回的对象就已经存在了!而且该对象
属于类,所以每次调用该方法,返回
的就是唯一的Waiter*/
returnwaiter;
}
int temp =0;
publicvoid kaiji(){
System.out.println("开机" + temp +"号电脑");
temp++;
}
}
其实这两种单例模式在开发中也并不是非常完美,但是作为现阶段,我们只需熟练编写上面两个案例即可,如果还有兴趣,可以参考下面链接的文章,还有其他创建单例对象的方式(按住ctrl键单击)。
http://www.runoob.com/design-pattern/singleton-pattern.html
思考案例:
情况1:
定义Person类,定义Man类,两个类不在同一个包下
package bao1;
public class Person{
protectedint age;
}
import bao1.Person;
public class Man
{
Person p = new Person();
publicvoid setAge(){
p.age= 12;
}
}
编译Man类会报错,因为age属性是protected修饰,必须是Person本包下或它的子类才可以访问,这里Man即不在同一个包下,也不是Person子类!
情况2:
定义Person类,定义Man类,两个类不在同一个包下
package bao1;
public class Person{
protectedint age;
}
import bao1.Person;
public class Man extends Person
{
Person p = new Person();
publicvoid setAge(){
p.age= 12;
}
}
这种情况还是会报错,因为虽然Man继承了Person,但是,实际上访问age属性的还是Person对象,这里并不满足Man类和Person类在同一包下,也不满足调用age属性的对象是Person对象的子类。这里的p指的是Person类型对象,不是他的子类对象。
情况3:
正确的情况:
定义Person类,定义Man类,两个类不在同一个包下
package bao1;
public class Person{
protectedint age;
}
import bao1.Person;
public class Man extends Person
{
Man p =new Man();
publicvoid setAge(){
p.age= 12;
}
}
这里,p.age是可以的,虽然Man不和Person在同一个包下,但是,创建出来访问age属性的对象是Person类型对象的子类对象,符合protected权限修饰符的访问范围,所以不报错!
情况4:
编写测试类TestPro,测试并思考:
package bao1;
public class Person{
protectedint age;
}
import bao1.Person;
public class Man extends Person
{
public intgetAge(){
returnthis.age;
}
publicvoid setAge(int age){
this.age= age;
}
}
class TestPro{
publicstatic void main(String[] args){
Manman = new Man();
man.setAge(123);
intage = man.getAge();
System.out.println(age);
}
}
这里通过测试类创建man对象,然后调用Man对象的age属性。
在man对象中,this代表的是当前对象,这个当前对象又是Person类的子类对象,所以是可以访问protected修饰的age属性,所以不会报错!!