面向对象——Robyn编程学习(Java)
面向对象
如何理解面向对象?
我的理解是程序就是客观世界的主观反映,在客观世界里我们根据动植物进行各种分类,同时又根据姓名等划分出一个个不同的对象个体,每个个体既有共性,也有自己的特性,而java则就是体现了这一思想,通过类来承载实例的特征。(写的多了就会运用随心)运用之妙,存乎一心。
面向对象大纲

面向对象基础
对象
对象在内存中的存在形式:

Java内存的结构分析
栈:一般存放基本数据类型(这里存放的是对象的引用)
堆:存放引用数据类型的地址(对象,数组)
方法区:存放常量池(字符串)和类加载信息
方法的调用机制分析

方法的传参机制
- 基本数据类型的传参机制
int a = 10;
int b = 20;
//创建 AA 对象 名字 obj
AAobj = newAA();
obj.swap(a, b); //调用 swap(这里是一个交换方法)
System.out.println("main 方法 a=" + a + " b=" + b);//a=10 b=20
得到结论:基本数据类型形参的改变不影响实参
- 引用类型的传参机制
//测试
B b = new B();
int[] arr = {1, 2, 3};
b.test100(arr);//调用方法(这里对数组进行了修改)
System.out.println(" main 的 arr 数组 ");
//遍历数组
// for(int i = 0; i < arr.length; i++) {
// System.out.print(arr[i] + "\t");
得到结论:引用类型传递的是地址(地址的值),可以通过形参影响实参
方法的递归调用
- 斐波那契数列问题
public int fibonacci(int n) {
if (n >= 1) {
if (n == 1 || n == 2) {
return 1;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);//此处体现递归思想
}
} else {
System.out.println("请输入n>=1的整数");
return -1;//代表程序运行失败
}
}
- 猴子吃桃问题
public int rabbit(int day) {
if (day == 10) {
return 1;
} else if (day >= 1 && day <= 9) {
return 2 * (rabbit(day + 1) + 1);
}else {
return -1;
}
}
递归问题关键就在于找到终结的值,然后让电脑自己判断。
作用域
全局变量:类中的变量 局部变量:方法中的变量
构造器的初始化

this关键字
- this 关键字可以用来访问本类的属性、方法、构造器
- this 用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
访问修饰符

两个比较有意思的练习题
//圆半径问题
public class Work6 {
public static void main(String[] args) {
//就是一个圆的习题(记得用匿名变量)
//封装的思想
class Circle{
double radius;
public double findArea(double radius){
return Math.PI * radius *radius;
}
}
class PassObject{
public void printAreas(Circle c,int times){
System.out.println("Radius"+"\t\t"+"Area");
for(double i = 1;i <= times;i++){
System.out.println(i+"\t\t"+c.findArea(i));
}
}
}
PassObject pass = new PassObject();
Circle cir = new Circle();
pass.printAreas(cir,5);
}
//体现的就是封装思想
猜拳游戏
这个题的逻辑就特别清晰,变量用来干什么,方法对于变量的修改和返回,数组作为对于结果的存储。以后写代码都应该这样,逻辑清晰。 写代码时就要有逻辑,用那些变量和那些方法,这个可以用LeetCode解决。
//实例化tom对象
Tom tom = new Tom();
int isWincount = 0;
//创建一个二维数组接收出拳情况
int arr[][] = new int[3][3];
int j = 0;
//创建一个一维数组接收输赢统计
String arr2[] = new String[3];
Scanner scanner = new Scanner(System.in);
for (int i = 0; i <3 ; i++) {
System.out.println("请出拳!");
int num = scanner.nextInt();
tom.setTomGuessNum(num);
int tomGuess = tom.getTomGuessNum();
arr[i][j+1] = tomGuess;
//获取电脑出拳
int comGuess = tom.computerNum();
arr[i][j+2] = comGuess;
String win = tom.vsComputer();
arr2[i] = win;
arr[i][j] = tom.count;
//对每一句的结果进行输出
System.out.println("=====");
System.out.println(tom.count+"\t"+tomGuess+"\t"+comGuess+"\t");
System.out.println("=====");
isWincount = tom.comWincount(win);//记录赢的次数,顺便改变局数
}
System.out.println("你最后赢了"+isWincount+"次!");
}
}
//创建一个玩家对象TOM
class Tom{
//设置玩家出拳
int tomGuessNum;
//设置电脑出拳
int comGuessNum;
int winCountNum;//记录赢的次数
int count = 1;//记录比赛的局数
public int getTomGuessNum() {
return tomGuessNum;
}
//首先设置tom的出拳
public void setTomGuessNum(int tomGuessNum) {
if(tomGuessNum > 2 || tomGuessNum < 0){
throw new IllegalArgumentException("数字输入错误");
}else {
this.tomGuessNum = tomGuessNum;
}
}
//设置电脑随机出拳
public int computerNum(){
Random random = new Random();
comGuessNum = random.nextInt(3);
return comGuessNum;
}
//判断比赛的胜负
public String vsComputer(){
if(tomGuessNum == 0 && comGuessNum == 1){
return "你赢了";
}else if(tomGuessNum == 1 &&comGuessNum == 2){
return "你赢了";
}else if(tomGuessNum == 2 &&comGuessNum == 0){
return "你赢了";
}else if(tomGuessNum == comGuessNum ){
return "平手";
}else{
return "你输了";
}
}
//记录比赛胜负次数
public int comWincount(String s){
count++;
if(s.equals("你赢了")){
winCountNum++;
}
return winCountNum;
面向对象特征
面向对象——封装
封装的好处就是数据没有办法更改,只能用已有的方法去更改数据
封装的三个步骤:
- 属性私有化
- 提供一个公共的set方法
- 提供一个公共的get方法访问
Account account = new Account();
account.setName("jack");
account.setBalance(60);
account.setPwd("123456");
就是构造器与get,set方法的结合
面向对象——继承
public class Computer {
private String cpu;
private int memory;
private int disk;
//创建构造器
public Computer(String cpu, int memory, int disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public int getMemory() {
return memory;
}
public void setMemory(int memory) {
this.memory = memory;
}
public int getDisk() {
return disk;
}
public void setDisk(int disk) {
this.disk = disk;
}
public String getDetail(){
return "电脑属性为:"+ cpu + memory + disk;
}
}
class PC extends Computer{
private String brand;
public PC(String cpu, int memory, int disk, String brand) {
super(cpu, memory, disk);
this.brand = brand;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void printInfo(){
System.out.println("pc信息为");
System.out.println(getCpu()+"\t"+getMemory()+"\t"+getDisk());
System.out.println(getDetail()+brand);
}
}
class AAA{
public static void main(String[] args) {
PC pc = new PC("levleo",16,500,"IBM");
pc.printInfo();
}
}
其实就是子类继承了父类的属性和方法,但是必须要在构造器中显式调用(父类是无参构造器除外),此外父类的私有属性必须通过公用方法来进行访问。
面向对象——多态
多态理论建立的基础

多态的几大注意事项
- 向上转型:父类类型 引用名 = new 子类类型();父类的引用指向子类的对象
- 向下转型:建立在向上转型的基础上,当父类的引用指向当前目标类型的对象时,就可以向下转型吧父类的引用转化为子类类型,从而使用子类类型中所有的成员。 子类类型 引用名 = (子类类型)父类引用。
Person[] persons = new Person[4];
persons[0] = new Student("jack", "男", 10, 001);
persons[1] = new Student("mary", "女", 20, 002);
persons[2] = new Teacher("smith", "男", 36, 5);
persons[3] = new Teacher("scott", "男", 26, 1);
//创建对象
Test homework13 = new Test();
homework13.bubbleSort(persons);
//输出排序后的数组
System.out.println("---排序后的数组-----");
for(int i = 0; i < persons.length; i++) {
System.out.println(persons[i]);
}
//遍历数组,调用test方法
System.out.println("=======================");
for (int i = 0; i < persons.length; i++) {//遍历多态数组
homework13.test(persons[i]);
}
}
//定义方法,形参为Person类型,功能:调用学生的study或教师的teach方法
//分析这里会使用到向下转型和类型判断
public void test(Person p) {
if(p instanceof Student) {//p 的运行类型如果是Student
((Student) p).study();
} else if(p instanceof Teacher) {
((Teacher) p).teach();
} else {
System.out.println("do nothing...");
}
}
//方法,完成年龄从高到底排序,这个冒泡排序太经典了
public void bubbleSort(Person[] persons) {
Person temp = null;
for (int i = 0; i < persons.length - 1; i++) {
for (int j = 0; j < persons.length - 1 - i; j++) {
//判断条件, 注意这里的条件可以根据需要,变化
if(persons[j].getAge() < persons[j+1].getAge()) {
temp = persons[j];
persons[j] = persons[j + 1];
persons[j + 1] = temp;
}
}
}
一个小思考:为什么main方法里不能有private修饰符呢
因为main方法是一个静态方法
方法内的变量是局部变量
局部变量只在定义它的内部有效,并且不能使用 private ,protected,public 修饰符进行修饰,用了也没有用,因为局部变量只在定义它的内部有效,当局部变量所在的方法调用结束后,java 虚拟机将自动释放局部变量所占用的资源。
多态的基础:动态绑定机制
当调用对象方法的时候,该对象会和对象的运行类型绑定
调用对象属性的时候,没有动态绑定机制,那里声明,哪里使用。(这也是为什么上面编译类型决定属性值的原因)
Sub sub = new Sub();
System.out.println(sub.count);
sub.display();
//向上转型
Base b = sub;//两个引用指向同一个对象
System.out.println(b == sub);
System.out.println(b.count);//编译类型
b.display();//运行类型
}
}
class Base{
int count = 10;
public void display(){
System.out.println(this.count);
}
}
class Sub extends Base{
int count = 20;
public void display() {
System.out.println(this.count);
在编写程序时,发现了一些细节,为什么有些地方必须得实例化,有些地方就不用实例化呢?还有困扰我的一点,和普通方法相比:main方法究竟有何特色之处,为何全部要调用?
这就引入了面向对象的高级部分:类变量与类方法
面向对象高阶:类变量与类方法
静态成员的调用
为啥要有类变量,因为有的变量是随着类加载时始终存在,不需要每次创建对象时都再重新创建,因此就可以设置为类变量(其实就是所有对象共享一个变量)
静态方法也是一样的道理:就是不需要实例化就可以直接运行,因此静态方法只能访问静态成员
为什么main()访问非静态成员,必须创建实例对象才可以,就可以从以下代码中寻找答案
private static String name = "汉合";//静态属性
private int n1 = 10000;//非静态方法
//静态方法
public static void hi(){
System.out.println("Main的hi方法");
}
//非静态方法
public void cry(){
System.out.println("main的cry方法");
}
//下面就是见证奇迹的时刻
public static void main(String[] args) {
System.out.println(name);
Main01 main01 = new Main01();//调用非静态方法必须实例化对象才行
System.out.println(main01.n1);//调用非静态属性才是一样
main01.cry();
// System.out.println(n1);
hi();
静态方法的调用

静态(类)方法与类加载相关,普通方法与对象有关,因此类方法中不能出现this和super,比如main方法不能出现this和super
!!!类加载的三种情况:
- 创建对象实例
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时
类加载时的访问顺序
#类加载时的顺序
①调用静态代码块和静态属性初始化
②调用普通代码块和普通属性初始化
#创建一个子类对象的调用顺序:
①父类的静态代码块和静态属性
②子类的静态代码块和静态属性
③父类的普通代码块和普通属性
④父类的构造方法
⑤子类的普通代码块和普通属性
⑥子类的构造方法
正是因为对于类变量的理解,我们对于Java中的main方法有了更深的理解。

设计模式(重点)
设计模式是在大量的实践中总结和理论化之后优选的代码结构,类似于棋谱。
下面以单例模式为设计要求
什么是单例模式?就是一个类中只存在一个对象实例,并且该类只提供一个可以获得其对象实例的方法。关键就是在这个类何时创建一个对象实例,这就可以区分出饿汉式和懒汉式设计模式。
//演示懒汉式,线程安全,但是存在资源浪费的情况
public class SingleTon01 {
public static void main(String[] args) {
//获取小红女朋友对象
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
}
}
//设置女朋友类
class GirlFriend{
private String name;
//一定要私有化,防止出现问题
private GirlFriend(String name) {
this.name = name;
}
//提前创建静态对象,方便调用
private static GirlFriend gf = new GirlFriend("小红");
//提供一个公共对象返回对象,注意这里也有静态方法比较好
public static GirlFriend getInstance(){
return gf;
}
//设置一个toString方法
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
懒汉式(需要时才创建,存在线程安全问题)
public class SingleTon01 {
public static void main(String[] args) {
//获取小红女朋友对象
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
}
}
//设置女朋友类
class GirlFriend{
private String name;
private static GirlFriend girlFriend;//这个对象需要时才实例化
//一定要私有化,防止出现问题
private GirlFriend(String name) {
this.name = name;
}
//提供一个公共对象返回对象,注意这里也有静态方法比较好,如果需要则创建一个对象
public static GirlFriend getInstance(){
if(girlFriend == null ){
girlFriend = new GirlFriend("小红红");
}
return girlFriend;
}
//设置一个toString方法
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
抽象类

因为方法要重写:所以一般都是用public,不可能用private/final/static修饰的
接口
//接口的调用很有意思,实现了接口就都是接口对象,就成为了接口对象,然后用向下转型来分别转型
MysqlDB mysqlDB = new MysqlDB();
t(mysqlDB);
OracleDB oracleDB = new OracleDB();
t(oracleDB);
}
public static void t(DBInterface db) {//跟USB接口有点像,传入一个插头对象,然后会自动识别传入的对象
db.connect();
db.close();
}
可以发现,接口就是自动识别传入的对象,从而实现相应的功能
接口多态性的思考(可以说:接口就是为了多态而生的)
Usb[] usb = new Usb[2];
usb[0] = new Phone();
usb[1] = new Camera();
//遍历数组从而得到特有的方法
for(int i = 0;i < usb.length;i++){
usb[i].work();//动态绑定机制
if(usb[i] instanceof Phone){
((Phone)usb[i]).call();
这不就是接口对象吗?果然体现的是多态的思想
请问实现接口和继承类有何区别呢
其实都是为了拓展功能,当子类继承父类,就拓展了父类的所有功能,如果再需要拓展功能,就可以通过接口的形式来拓展。
匿名内部类
public class InnerClassExercise {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
Bell bell = new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
};
cellPhone.alarmClock(bell);
}
}
interface Bell{//接口
void ring();//方法
}
class CellPhone{
public void alarmClock(Bell bell){//传入实现接口的接口对象
System.out.println(bell.getClass());
bell.ring();
}
}
单例设计模式的应用
public class InnerClassExercise {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
Bell bell = new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
};
cellPhone.alarmClock(bell);
}
}
interface Bell{//接口
void ring();//方法
}
class CellPhone{
public void alarmClock(Bell bell){//传入实现接口的接口对象
System.out.println(bell.getClass());
bell.ring();
}
}
把需求给转化I成功能函数,这个真的很有意思,以后写代码也要注意这方面的应用!

浙公网安备 33010602011771号