面向对象封装性
- 引入封装
- 为什么要封装
- 封装的好处
User对象
package com.bjpowernode.javase.day08.test003;
public class User {
int age;
}
UserTest测试对象
package com.bjpowernode.javase.day08.test003;
/**
用户测试类
对于当前程序来说:User类中的age属性在外部程序中可以随意访问,导致age属性的不安全。
一个User对象表示一个用户,用户的年龄不可能为负数,以下程序当中年龄值为负数,程序运行
时并没有报错。这是当前程序中存在的缺陷。
面向对象包括三大特征:
- 封装
- 继承
- 多态
当前主要讲解的是封装机制。为什么要封装?封装有什么好处?
封装的好处:
1.封装之后,对于那个事物来说,看不到这个事物比较复杂的那一面,只能看到该事物简单的那一面。
复杂性封装,对外提供简单的操作入口。照相机就是一个很好的封装的案例,照相机的实现原理非常复杂,
但是对于使用照相机的人来说,操作起来是非常方便的,是非常快捷的。还有像电视机也是封装的,电视机内存
实现非常复杂,但是对于使用者来说只需会使用遥控器即可。
2.封装之后才会形成真正的“对象”,真正的“独立体”
3.封装就意味着以后的程序可以重复使用。并且这个事物适应性比较强,在任何场合都可以使用。
4.封装之后,对于事物本身,提高了安全性。【安全级别高】
*/
public class UserTest {
public static void main(String[] args) {
// 创建User对象
User user = new User();
// 访问age
// 读取年龄值【get】
System.out.println("该用户年龄:" + user.age);
// 修改年龄值【set】
user.age = 20;
// 读取年龄值【get】
System.out.println("该用户年龄:" + user.age);
// 修改年龄值【set】
// 这里的age属性是完全暴露给外部程序的,对于程序员来说可以操作User对象当中所有的细节,导致User中部分数据不安全
// 不建议这样,建议User类型进行封装,建议在外部程序中不能随意访问User对象当中的属性。这样可以保证属性的安全。
user.age = -100;
// 读取年龄值【get】
System.out.println("该用户年龄:" + user.age);
}
}
- 封装的步骤
User对象
package com.bjpowernode.javase.day08.test004;
/**
封装的步骤:
1.所有属性私有化,使用private关键字进行修饰,private表示私有化,修饰的所有数据只能在本类中进行访问
2.对外提供简单的操作入口,也就是以后外部程序要想访问age属性,必须通过这些简单的入口进行访问
3.set方法的命名规范:
public void setAge(int a){
age = a;
}
4.get方法的命名规范:
public int getAge(){
return age;
}
回想以下,一个属性通常访问的时候包括几种访问形式?
- 第一种方式:想读取这个属性的值,读取get
- 第二种方式:想修改这个属性的值,修改set
需要大家先背会以下内容:
setter anf getter方法没有static关键字;
有static关键字修饰的方法怎么调用:类名.方法名(实参);
没有static关键字修饰的方法怎么调用:引用.方法名(实参);
*/
public class User {
//属性私有化
private int age;
//set方法没有返回值,因为set方法只负责修改数据
/*
public void setAge(int age) {
age = age;//java有就近原则,这里并没有给age属性赋值,这里的age都是局部变量age
}
*/
public void setAge(int a) {
//编写业务逻辑代码进行控制
//age = a;
if(a < 0 || a > 150) {
System.out.println("对不起,你提供的 年龄不合法");
return;
}
age = a;
}
public int getAge() {
return age;
}
}
UserTest测试对象
package com.bjpowernode.javase.day08.test004;
public class UserTest {
public static void main(String[] args) {
// 创建User对象
User user = new User();
// 编译报错,age属性私有化,在外部程序中不能直接访问
// 从此之后age属性非常的安全,但是有点太安全了。
// 对于目前的程序来说,age属性在外部访问不到了
// System.out.println(user.name);
//修改
user.setAge(-100);
//读取
System.out.println(user.getAge());
}
}
- eclipse快速给类中私有变量添加相应的setter和getter方法,步骤如下:
代码位置右键 --> Source --> Generate Getters and Setters
package com.bjpowernode.javase.day08.test004;
//顾客类
public class Customer {
//属性
private int id;
private String name;
private int age;
private String addr;
//setter and getter方法
public int getId() {
return id;
}
public void setId(int id) {
//安全控制
this.id = id;
}
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 String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
package com.bjpowernode.javase.day08.test004;
public class CustomerTest {
public static void main(String[] args) {
Customer c = new Customer();
//私有的属性不能在外部直接访问
//c.id = 100;
//操作入口变成了只能通过set和get方法进行访问
//在set方法和get方法执行过程中可以进行安全过滤
c.setId(101010);
c.setAge(100);
c.setName("zhangsan");
c.setAddr("青岛市黄岛区辛安街道");
System.out.println(c.getId());
System.out.println(c.getName());
System.out.println(c.getAge());
System.out.println(c.getAddr());
}
}
构造方法
- 引入构造方法
package com.bjpowernode.javase.day08.test005;
public class User {
//无参数构造方法
public User() {
System.out.println("User's Default Constructor Invoke!");
}
//有参数的构造方法
public User(int i) {
System.out.println("带有int类型参数的构造器");
}
//有参数的构造方法
public User(String i) {
System.out.println("带有String类型参数的构造器");
}
//有参数的构造方法
public User(int i,String j) {
System.out.println("带有int,String类型参数的构造器");
}
}
package com.bjpowernode.javase.day08.test005;
/**
关于java类中的构造方法:
1.构造方法又称:构造函数 / 构造器 / Constructor
2.构造方法语法结构:
【修饰符列表】构造方法名(形式参数列表){
构造方法体;
}
3.回顾方法体结构:
【修饰符列表】返回值类型 方法名(形式参数列表){
方法体;
}
4.对于构造方法来说,“返回值类型”不需要指定,并且也不能写void,
只要写上void,那么这个方法就变成普通方法了。
5.对于构造方法来说,构造方法的方法名必须和类名保持一致
6.构造方法的作用?
构造方法存在的意义是,通过构造方法的调用,可以创建对象
7.构造方法应该怎么调用?
普通方法是这样调用的:
方法修饰符中有static的时候: 类名.方法名(实参列表)
方法修饰符中没有static的时候:引用.方法名(实参列表)
构造方法是这样调用的:
new 构造方法名(实参列表)
8.构造方法结束之后,有返回值吗?
每一个构造方法实际上执行结束之后都有返回值,但是这个"return 值;"这样的语句不需要写,
构造方法结束的时候java程序自动返回值,并且返回值类型就是类本身,所以返回值类型不需要编写。
9.eclipse快捷键:单行注释:ctrl + / ; 多行注释:ctrl + shift + /
10.当一个类中没有定义任何构造方法的话,系统默认给该类提供一个无参数的构造方法,这个构造方法被称为缺省构造器
11.当一个类显示的将构造方法定义出来了 ,那么系统不再默认为这个类型提供缺省构造器。建议开发中手动的为当前类提供
无参数构造方法,因为无参数构造方法太常用了。
12.构造方法支持重载机制,在一个类中编写多个构造方法,这多个构造方法显然已经构成方法重载机制。
*/
public class ConstructorTest01 {
public static void main(String[] args) {
//创建User对象
//使用User类的构造方法来完成对象的创建
//以下程序创建了4个对象,只要构造函数调用就会创建对象,并且一定是在“堆内存”中开辟内存空间。
User u1 = new User();
User u2 = new User(10);
User u3 = new User("zhangsan");
User u4 = new User(10,"zahngsan");
// int i = sumInt(100);
//普通方法中,修饰符列表有static,调用形式:类名.方法名
ConstructorTest01.doSome();
doSome();//同一个类中可以省略类名
//普通方法中,修饰符列表中没有static ,调用形式:引用.方法名
//doOther方法在ConstructorTest01类当中,所以需要创建ConstructorTest01对象
//创建ConstructorTest01对象,调用无参数构造方法
ConstructorTest01 t = new ConstructorTest01();//一个类中没有任何构造方法的话,系统默认提供一个无参数构造器
t.doOther();
}
public static void doSome() {
System.out.println("do Some!");
}
public void doOther() {
System.out.println("do Other!");
}
}
- 构造方法的作用
package com.bjpowernode.javase.day08.test005;
// 账户类
public class Account {
//账号
private String actno;//实例变量/对象变量,也就是说,必须先有对象才能有对应的实例变量,
//余额
private double balance;
//无参构造器
public Account() {
//初始化实例变量的内存空间
//actno = null;
//balance = 0.0;
}
public Account(String s) {
actno = s;
//balance = 0.0;
}
public Account(double d) {
//actno = null;
balance = d;
}
public Account(String s,double d) {
actno = s;
balance = d;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
package com.bjpowernode.javase.day08.test005;
/**
构造方法的作用:
1.创建对象
2.初始化实例变量
成员变量之实例变量,属于对象级别的变量,这种变量必须先有对象才能有实例变量
实例变量没有手动赋值的时候,系统默认赋值,那么这个系统默认赋值是在什么时候完成的呢?
是在类加载的时候吗?
不是,因为类加载的时候只将字节码文件和代码片段加载到方法区内存,还没来得及创建对象。
所以此时实例变量并没有初始化。
实际上,实例变量的内存空间是在构造方法执行过程中完成开辟的,完成初始化的。
系统在默认赋值的时候,也是在构造方法执行过程中完成的赋值。
实例变量默认赋值:
byte,short,int,long 0
float,double 0.0
boolean false
引用数据类型 null
实例变量存储在JVM的堆内存java对象的内部
*/
public class ConstructorTest02 {
public static void main(String[] args) {
//在eclipse当中怎么查看访问的是哪个属性,查看访问的是哪个方法?
//按ctrl键,鼠标移动到查看的元素上,出现下划线的时候开始单击。
//另外,在一个类中元素过多,想快速查看,在当前类中使用ctrl + o快捷键,然后输入要查找的元素名称,该名称不一定输入全名称
//创建对象
Account acc1 = new Account();
System.out.println("账号:" + acc1.getActno());
System.out.println("余额:" + acc1.getBalance());
Account acc2 = new Account("111");
System.out.println("账号:" + acc2.getActno());
System.out.println("余额:" + acc2.getBalance());
Account acc3 = new Account(100.0);
System.out.println("账号:" + acc3.getActno());
System.out.println("余额:" + acc3.getBalance());
Account acc4 = new Account("acc4-1",200.0);
System.out.println("账号:" + acc4.getActno());
System.out.println("余额:" + acc4.getBalance());
}
}
- 利用系统快捷方式快速生成构造方法
代码位置右键 --> Source --> Generate Constructor using Fields… - 快捷键,按列同步更改代码:快捷图标
package com.bjpowernode.javase.day08.test005;
public class Customer {
String name;
int no;
String addr;
//无参数构造器
public Customer() {
}
//有参数构造器
public Customer(String a, int b, String c) {
name = a;
no = b;
addr = c;
}
public Customer(int no) {
super();
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
package com.bjpowernode.javase.day08.test005;
public class ConstructorTest03 {
public static void main(String[] args) {
Customer c1 = new Customer();
System.out.println(c1.getNo());
System.out.println(c1.getName());
System.out.println(c1.getAddr());
Customer c2 = new Customer("zhangsan",111,"青岛市");
System.out.println(c2.getNo());
System.out.println(c2.getName());
System.out.println(c2.getAddr());
}
}
对象和引用
- 对象:目前在使用new运算符在堆内存中开辟的内存空间称为对象。
- 引用:是一个变量,不一定是局部变量,还可能是成员变量。引用保存了内存地址,指向了堆内存当中的对象。
- 所有访问实例相关的数据,都需要通过“引用.”的方式访问,因为只有通过引用才能找到对象。
- 只有一个空的引用,访问对象的实例相关的数据会出现空指针异常。
class Student{
Computer com;//com是一个引用【实例变量】
public static void doSome(){
Computer cc;//cc是一个引用【局部变量】
}
}
参数传递
- 主要研究和学习的是方法在调用的时候,涉及到参数传递的问题,到底是怎么传递数据的呢?
int i = 10;
int j = i;//i传递给j,实际上是将i变量中保存的10传递给j了,j实际上是一块全新的内存空间。
User u = 0x1234;
User u2 = u;//u传递给u2,实际上是将0x1234这个值赋值给u2了,u和u2实际上是两个不同的局部变量
//但是它们这两个变量指向堆内存中同一个java对象
- java语言当中调用的时候涉及到参数传递的问题
参数传递的是变量中的具体值
package com.bjpowernode.javase.day08.test006;
/**
java语言当中调用的时候涉及到参数传递的问题
参数传递的是变量中的具体值
int i = 10;
add(i); 等同于 add(10)
*/
public class Test01 {
public static void main(String[] args) {
int i = 10;
add(i);//add方法调用的时候,给add方法传递了一个变量i,到底传的是什么?
System.out.println("main -->" + i);
}
public static void add(int i) {
i ++;
System.out.println("add -->" + i);
}
}
- 内容2
package com.bjpowernode.javase.day08.test006;
public class Test02 {
public static void main(String[] args) {
//User u = 0x1234;
//传递u给add方法的时候,实际上传递的是u变量中保存的值,只不过这个值是一个java对象的内存地址
User u = new User(20);
add(u);//等同于:add(0x1234);
System.out.println("main -->" + u.age);//21
}
public static void add(User u) {
u.age ++;
System.out.println("add -->" + u.age);//21
}
}
class User{
//实例变量
int age;
//构造方法
public User(int i) {
age = i;
}
}