类与对象
1.面向过程与面向对象思想
面向过程
当我们在解决一个问题时,我们会按照预先设定的想法和步骤,一步一步去实现。在这里每一步具体的实现中都需要我们亲自实现和操作。
我们的角色是:执行者
特点:费时间,费精力,结果也不一定完美
面向对象
当我们在解决一个问题时,可以去找具有相对应的功能的事物帮我们去解决问题,至于这个事物如何工作,与我们无关,我们只看结果,不关心流程。
我们的角色是: 指挥者
特点:对于我们而言,节省时间,节省精力,结果相对完美
面向对象三大特征:封装、继承、多态
面向过程是面向对象的基础,问题可以自己不解决,托给别人解决,别人也可以再托给别人,但最终,事情必须要被处理掉,一旦处理就是面向过程
面向对象和面向过程差异
- 面向对象是一种符合人们思考习惯的思想
- 面向过程中更多的体现的是执行者,而在面向对象中更多的体现是指挥者
- 面向对象可以将复杂的问题进行简单化,更加贴近真实的社会场景
2.类与对象的关系
什么是对象
面向对象编程语言主要是使用对象们来进行相关的变成。对象,万事万物中存在的每一个实体。
如何去描述一个对象的内容?
- 对象的属性:就是对象相关的参数,可以直接用数据来衡量的一些特征——常量|变量来表述(成员变量)
- 对象的行为:就是通过将对象的属性进行联动,产生出一系列的动作或行为——函数(成员函数)
什么是类
类是具有相同属性特征和行为的对象们的统称。对象就是该类描述下具体存在的一个事物。
示例:定义一个圆类
//这是一个专门用于程序执行的类
//包含主函数的类 统称为 主类
//当然 实体类中也可以包含主函数
public class Test{
public static void main(String[] args){
Circle c1 = new Circle();
System.out.println(c1.radius);
c1.radius = 10;
System.out.println(c1.getArea());
Circle c2 = new Circle();
System.out.println(c2.radius);
System.out.println(c2.getArea());
Circle c3 = new Circle();
c3.setRadius(1);
System.out.println(c3.getPerimeter());
}
}
//这是用于描述圆的一个类
//类似于这种专门描述事物的类--实体类
class Circle{
//属性
double radius;
//行为
public double getArea(){
return Math.PI * radius * radius;
}
public double getPerimeter(){
return 2 * Math.PI *radius;
}
public void setRadius(double newRadius){
radius = newRadius;
}
}
示例:定义一个电视机类
public class Test{
public static void main(String[] args){
TV tv1 = new TV();
tv1.turnOn();
tv1.setChannel(12);
tv1.setChannel(200);
tv1.channeUP();
tv1.channelDowm();
tv1.showStatues();
}
}
class TV{
//属性
int channel = 1;
int volumeLevel = 3;
boolean on;
//行为
public void turnOn(){
on = true;
}
public void turnOff(){
on = false;
}
public void setChannel(int newChannel){
if(on) {
if(newChannel > 120){
channel = 120;
}else if(newChannel < 1){
channel = 1;
}else {
channel = newChannel;
}
System.out.println("电视选台为:" + channel);
}
}
public void setVoluneLevel(int newVolumeLevel){
if(on) {
if(volumeLevel > 7){
volumeLevel = 7;
}else if(volumeLevel < 1){
volumeLevel = 1;
}else {
volumeLevel = newVolumeLevel;
}
System.out.println("电视音量为:" + volumeLevel);
}
}
public void channeUP(){
if(on) {
channel++;
if(channel > 120);{
channel = 1;
}
System.out.println("电视选台为:" + channel);
}
}
public void channelDowm(){
if(on) {
channel--;
if(channel == 0) {
channel = 120;
}
System.out.println("电视选台为:" + channel);
}
}
public void volumeUp(){
if(on){
volumeLevel++;
if(volumeLevel > 7){
volumeLevel = 7;
}
System.out.println("电视音量为:" + volumeLevel);
}
}
public void volumeDown(){
if(on){
volumeLevel--;
if(volumeLevel < 1){
volumeLevel = 1;
}
System.out.println("电视音量为:" + volumeLevel);
}
}
public void showStatues(){
if(on){
System.out.println("电视已开机");
System.out.println("频道" + channel);
System.out.println("音量" + volumeLevel);
}else{
System.out.println("电视已经关机");
}
}
}
3.封装与private关键字
封装:隐藏事物的细节,对外提供公开的访问方式
常见的封装体现
- 函数
- 类
封装有什么好处?
- 提高了安全性
- 向外界隐藏了一些不需要被外界获知的内容
- 提高代码的复用性
- 也是面向对象的三大特点之一
封装不是封死,还是要向外提供一些访问内部内容的方式
private关键字,属于权限关键字 <public portected 默认不写 private>
private可以作用在对象属性和行为上,外界在创建对象后,则不能访问被private修饰的内容
public class Test{
public static void main(String[] args){
Person p1 = new Person();
p1.setName("旺财");
p1.setAge(-10);
p1.speak();
System.out.println(p1.getAge());
}
}
class Person{
private String name;
private int age;
public void speak(){
System.out.println("我是" + name + "我今年" + age +"岁");
}
public void setName(String newName) {
if(newName.equals("旺财")){
name = "哈士奇";
}else {
name = newName;
}
}
public void setAge(int newAge) {
if(newAge < 0) {
age = 0;
}else{
age = newAge;
}
}
public String getName(){
return name;
}
public int getAge() {
return age;
}
}
总结:
- 类中不需要对外界提供的内容最好私有化
- 如果后续真的需要对私有的内容进行更改,最好加上setXXX修改器,getXXX访问器
不可变对象或类
就是指其内部的数据都是私有的,没有向外界提供任何修改内部的方法(String)
- 所有的数据都是私有的
- 没有修改器的方法
4.局部变量与成员变量
public class Test{
public static void main(String[] args){
Person p1 = new Person();
p1.setName("小强");
p1.setAge(10);
p1.speak();
}
}
class Person{
private String name;
private int age;
public void speak(){
System.out.println("我是" + name + "我今年" + age +"岁");
}
public void setName(String name) {
if(name.equals("旺财")){
this.name = "哈士奇";
}else {
this.name = name;
}
}
public void setAge(int age) {
if(age < 0) {
this.age = 0;
}else{
this.age = age;
}
}
public String getName(){
return name;
}
public int getAge() {
return age;
}
}
代码执行流程:
1.javac编译Test.java源代码生成Test.class和Person.clsaa两个字节码文件
2.如果java Person,运行Person字节码文件,则报错,没有主函数不是主类
3.只能java Test 运行Test程序
4.将相关的的字节码(Test.class Person.class)文件加载进JVM中内存下的方法区
5.在方法区中Test字节码所在的区域里,找主函数,将主函数的栈帧加载进栈内存开始运行
6.开始执行主函数的第一句代码,创建一个Person对象
7.在堆内存中开辟一个空间并分配地址,在该空间中去创建成员变量,并默认初始化
8.在主函数空间创建的局部变量p1,并将该对象的地址传给p1
9.接着执行主函数第二局代码,调用p1对象的setName方法
10.从方法区中的Person里,将getName函数栈帧加载进栈,主函数暂停运行
11.setName进站后,创建局部变量name(形参),将实参“小强”这个字符串在字符串常量池中的地址赋给name
12.因为setName成员函数只有一份在方法区中Person所属区间里,之后可以被多个同类对象调用,为了区分到底是哪个对象调用的该方法,在每一个成员函数中,都会有一个隐藏的关键字数据this
,this相当于一个变量来存储当前对象的地址。(当前对象的引用)
13.执行setName中的内容,如果数据没有问题,就将局部变量的值赋值给当前对象的成员变量
14.setName函数执行最后一行隐藏的return,表示函数结束并且弹栈
15.主函数成为当前栈顶继续执行
16.执行p1调用setAge函数,从方法区中Person所属空间中找setAge所属代码,将函数加载进栈内存,成为新的栈顶,则主函数暂停运行,setAge开始运行。先创建形参age的局部变量,接收实参传来的值,为了区分对象的调用关系,自带this关键字数据,this存的还是p1的地址,如果age没有问题,则将10传给this所指向的对象中age这个成员变量。setAge执行最后一行隐藏的return,表示函数结束并弹栈
17.主函数成为新的栈顶继续运行,调用p1的speak函数进栈
18.在方法区中Person字节码所属空间里读取speak代码,将该栈帧加载进栈内存中,主函数暂停,该函数执行,无形参只能表示没有形式参数的局部变量,但是在函数内部也可以创建其他的局部变量,并且有this关键字指向p1的地址,然后打印name和age,由于speak空间已经没有其他名为name和age的局部变量,接着找this对象p1中的数据,找到了则打印,直至函数结束并弹栈
19.主函数又成为栈顶,执行隐藏的return,主函数弹栈,表示程序结束
局部变量和成员变量有什么区别?
- 生命周期
- 成员变量随着对象的创建而创建,随着对象的消亡而消失
- 局部变量随着函数的创建而创建,随着函数的出栈消失
- 存储位置
- 成员变量存储在堆内存中对象所属的空间里
- 局部变量存储在栈内存中函数所属的空间里
- 定义位置
- 成员变量在类中,函数外定义
- 局部变量在函数中定义
- 初始化
- 成员变量有默认初始化值
- 局部变量必须初始化之后再调用
5.构造函数
- 在创建对象的时候,对象的成员变量是在对象创建之后通过setxxx修改器赋值
- 对对象成员变量的赋值可以进行可选操作
什么是构造函数:构造函数主要是在创建对象的时候执行的函数,在该函数中也可以对成员变量进行一些操作
构造函数的格式
权限修饰符 类名(参数列表){
构造函数的代码块
}
- 构造函数没有返回值
- 构造函数的名称必须是类名
- 参数列表是可选的,构造函数是可以重载的
- 虽然构造函数没有返回值,还是存在return关键字的
- 当我们的类中没有定义任何构造函数时,会有一个默认隐藏的无参构造函数存在
- 构造函数和成员函数一样,为了区分对象的调用,构造函数也自带this关键字
构造函数需要注意的问题
- 如果一旦定义其他参数列表的构造函数的话,这个隐藏的无参构造函数就会消失,所以建议手写出来
- 构造函数只有在创建对象的时候执行,当对象创建完毕,该对象的构造函数则不能执行
- 成员函数只有在对象创建之后才能执行
- 成员函数能否直接调用构造函数?不能调用,报找不到符号的错误,会误认为是同名的成员函数
- 构造函数能否直接调用成员函数?可以调用,但是这些成员函数一般是构造函数的部分代码片段被切割粗胡来了而已,从语意上而言,不属于对象的特有行为(也有特例)所以这些函数就是成员函数的样子,但没有必要向外界提供访问,所以加上private
- 构造函数能否直接调用构造函数?可以调用,但是必须通过
this(参数列表)
,构造函数可以单向调用其他构造函数,但是不能回调 - 构造函数是在创建对象的时候执行的,可以在期间对成员变量进行初始化,问:setXXX还需要不?看需求,如果后期成员变量需要修改则提供setXXX修改器
关于成员变量初始化问题
成员变量的初始化经历了三个步骤:默认初始化(大家默认都是0值),显示初始化(大家的值都一样,针对性初始化(大家的值可选)
6.对象的创建流程
示例:定义一个栈
public class Test {
public static void main(String[] args) {
Stack stack = new Stack();
System.out.println(stack);
for (int i = 1;i <= 10;i++){
stack.push(i);
}
stack.push(11);
System.out.println(stack.toString());
System.out.println(stack.pop());
System.out.println(stack);
System.out.println(stack.peek());
}
}
class Stack{
private int[] data;//栈的容器
private int top = -1;//栈顶元素角标 开始为-1
private static int capacity = 10;//栈容器的 最大容量 top+1 <= capacity
public Stack(){
this(capacity);
}
public Stack(int capacity){
data = new int[capacity];
}
//向栈中进栈一个元素e
public void push(int e){
if(size() == data.length){
//需要扩容
resize(data.length * 2);
}
top++;
data[top] = e;
}
public int pop(){
if(isEmpty()){
System.out.println("栈已空,无法弹出元素");
return -1;//表示出错
}
int e = data[top];
top--;
if(size() == data.length / 4 && data.length > capacity){
resize(data.length / 2);
}
return e;
}
public int peek(){
if(isEmpty()){
System.out.println("栈已空,无法获取栈顶元素");
return -1;//表示出错
}
return data[top];
}
private void resize(int len){
int[] arr = new int[len];
for(int i = 0;i <= top;i++){
arr[i] = data[i];
}
data = arr;
}
//获取有效元素
public int size(){
return top + 1;
}
//判断栈是否为空
public boolean isEmpty(){
return top == -1;
}
//打印一个对象其实就是在打印这个对象toString方法的结果
public String toString(){
if(isEmpty()){
return "[]";
}else{
String s = "[";
for(int i = 0 ;i <= top;i++){
if(i == top){
s = s + data[i] + "]";
}else{
s = s + data[i] + ",";
}
}
return s;
}
}
}
示例:模拟吃鸡游戏
public class Test {
public static void main(String[] args) {
Player p1 = new Player("小米",100);
Player p2 = new Player("小红",100);
p1.shootEnemy(p2);
Gun gun = new Gun();
p1.holsGun(gun);
p1.shootEnemy(p2);
Clip clip = new Clip();
for(int i = 1;i <= 30;i++){
clip.pushBullet(new Bullet());
}
p1.loadClip(clip);
for(int i = 1;i <= 30;i++){
p1.shootEnemy(p2);
}
p1.shootEnemy(p2);
}
}
class Player{
private String name;
private int blood;
private Gun gun;
public Player(){}
public Player(String name,int blood){
this.name = name;
this.blood = blood;
}
public Player(String name,int blood,Gun gun){
this.name = name;
this.blood = blood;
this.gun = gun;
}
public void holsGun(Gun gun){
this.gun = gun;
}
public void shootEnemy(Player enemy){
if(gun == null){
System.out.println("玩家信息:没有枪,开不了");
}else {
System.out.printf("%s向%s开了一枪\n",name,enemy.name);
gun.shootEmeny(enemy);
}
}
public void loadClip(Clip clip){
if(gun == null){
System.out.println("没枪,装不了");
}else {
gun.loadClip(clip);
}
}
public void damage(int hurt){
if(blood == 0){
System.out.println("玩家信息"+name+"已经成盒,不要打尸体啦");
}else {
blood -= hurt;
if(blood > 0){
System.out.println("玩家信息"+name+"掉血" + hurt + ",剩余" +blood );
}else{
blood = 0;
System.out.println("玩家信息"+name+"已经成盒,不要打尸体啦");
}
}
}
}
class Gun{
private Clip clip;
public Gun(){
this(null);
}
public Gun(Clip clip){
this.clip = clip;
}
public void loadClip(Clip clip){
this.clip = clip;
}
public void shootEmeny(Player enemy){
if(clip ==null){
System.out.println("枪的信息:没有弹夹");
return;
}
Bullet bullet = clip.popBullet();
if(bullet == null){
System.out.println("枪的信息,弹夹没子弹");
}else {
bullet.hitEnemy(enemy);
}
}
}
class Clip{
private int capacity = 30;
private int surlplus = 0;
private Bullet[] magazine;
public Clip(){
this(30);
}
public Clip(int capacity){
this.capacity = capacity;
magazine = new Bullet[capacity];
}
public void pushBullet(Bullet bullet){
if (surlplus == capacity){
System.out.println("弹夹已满,无法装入子弹");
return;
}
magazine[surlplus] = bullet;
surlplus++;
showClip();
}
public Bullet popBullet(){
if(surlplus == 0){
System.out.println("弹夹已空,无法弹出子弹");
return null;
}
Bullet bullet = magazine[surlplus - 1];
surlplus--;
showClip();
return bullet;
}
public void showClip(){
System.out.printf("弹夹信息:%d/%d\n",surlplus,capacity);
}
}
class Bullet{
private int hurt = 10;
public Bullet(){};
public Bullet(int hurt){
this.hurt = hurt;
}
public void hitEnemy(Player enemy){
enemy.damage(hurt);
}
}
7.static关键字
静态关键字
主函数有static修饰——静态函数,全局变量static修饰
主要用于修饰成员变量(对象的特有属性)和成员函数,变为静态变量和静态函数
静态变量最大的特点:同类下多个对象之间的共有属性
什么时候定义静态变量?在同一类下,多个对象之间有相同的属性和值,那么就可以将该属性和值,从成员变量变为静态变量,目的就是为了节省空间。
什么时候去定义静态函数?只有一点,当一个成员函数不访问成员时,即可定义为静态函数
静态函数一旦定义,可以直接用类名去调用(Math.sqrt()),当然也可以通过创建对象来去调用静态函数
静态优先于对象存在,且在同一个类中静态无法访问非静态(成员),非静态是可以访问静态的
静态函数中不存在this关键字
当通过对象去调用一个属性时,先找成员,再找静态,最后找父类
如果是从成员函数调用一个属性,先找局部,再找成员,再找静态,最后找父类
好处:
- 节省堆内存中的空间
- 可以不用费力气去创建对象来调用功能
- 可以对类进行一些初始操作(结合代码块来做)
public class Test {
public static void main(String[] args) {
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
Chinese c3 = new Chinese();
System.out.println(c1.country);
System.out.println(Chinese.country);
}
}
class Chinese{
String name;
int age;
static String country;
//静态代码块
static {
country = "China";
System.out.println("static...");
}
Chinese(){
System.out.println("Chinese...");
}
}
8.静态变量与成员变量
存储位置
成员变量存储与堆内存中对象所属的空间
静态变量存储在静态方法区中对应的字节码空间
生命周期
成员变量随对象的创建而创建,对着对象的消亡而消亡
静态变量随着类的加载而存在,随着程序的结束而消失
所属不同
成员变量是属于对象的,称之为对象的特有属性
静态变量是属于类的,称之为类的属性,或者叫对象的共有属性
调用方式不同
成员变量在外界必须通过创建对象来调用,在内部成员函数可以直接调用成员变量,但是静态函数不能直接调用成员变量,如果非要在静态函数中调用成员,只能创建对象,通过对象调用
静态变量在外界可以通过对象调用,也可以通过类来调用,在内部话静态函数/成员函数可以调用静态变量
public class Test {
public static void main(String[] args) {
//报错:StackOverflowError 栈内存溢出
A a = new A();
}
}
class A{
//OutOfMemoryError 堆内存溢出
int[][] arr = new int[1024][1024];
A a = new A();
}
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println(a == a.a);//flase
System.out.println(a.a == a.a.a);//true
}
}
class A{
static A a = new A();
}
着类的加载而存在,随着程序的结束而消失
所属不同
成员变量是属于对象的,称之为对象的特有属性
静态变量是属于类的,称之为类的属性,或者叫对象的共有属性
调用方式不同
成员变量在外界必须通过创建对象来调用,在内部成员函数可以直接调用成员变量,但是静态函数不能直接调用成员变量,如果非要在静态函数中调用成员,只能创建对象,通过对象调用
静态变量在外界可以通过对象调用,也可以通过类来调用,在内部话静态函数/成员函数可以调用静态变量
public class Test {
public static void main(String[] args) {
//报错:StackOverflowError 栈内存溢出
A a = new A();
}
}
class A{
//OutOfMemoryError 堆内存溢出
int[][] arr = new int[1024][1024];
A a = new A();
}
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println(a == a.a);//flase
System.out.println(a.a == a.a.a);//true
}
}
class A{
static A a = new A();
}