说到面向对象,我们又会想到面向过程,这两者有何区别呢?
一、了解相关概念
1.面向过程(POP)Prodcedure Oriented Programming和 面向对象(OOP)Object Oriented Programming 二者都是一种思想,面向对象是相对于面向过程而言的,面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
2.面向对象更加强调运用人类在日常生活中的思维逻辑中采用的思想方法和原则,如抽象,分类,继承,聚合,多态等等
3.面向对象的三大特征
* 封装(Encapsulation)
* 继承(Inheritance)
* 多态(polymorphism)
*
4.类和对象是面向对象的核心概念
* 类:对一类事物的描述,是抽象的,概念上的定义
* 对象:是实际存在该类事物的每个个体,因而也被称为实例(instance)
* 可以理解为:类 = 抽象概念的人; 对象= 实实在在的人
* 面向对象程序设计的重点是类的设计
* 类的设计,其实就是类的成员的设计
*
5.类的组成部分:
* 属性 = 成员变量
* 行为 = 方法 = 函数
6.本篇章主要是与对象的封装性有关
7.jvm内存结构
8.对象的内存结构
9.对象数组的内存结构图
二、类的属性
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/1/26
* @Description
* @Version 1.0
*/
/**
* 类中属性的使用
* 属性(成员变量) vs 局部变量
* 1.相同点:
* 1.1 定义变量的格式:数据类型 变量名 = 变量值
* 1.2 先声明,后使用
* 1.3 变量都有其对应的作用域
*
*
* 2.不同点:
* 2.1 在类中的声明位置不同
* 属性:直接定义在类的一对{}内
* 局部变量:声明在方法内,方法形参,代码块内,构造器形参,构造器内部的变量,
*
* 2.2 关于权限修饰符的不同
* 属性: 可以在声明属性时,指明其权限,使用权限修饰符
* 常用的修饰符:private public 缺省 protected
* 局部变量:不可以使用权限修饰符
*
* 2.3 默认初始化值的情况
* 属性:类的属性,根据其类型,都有默认初始化值
* 整形(byte,short,int,long)0
* 浮点型(float,double)0.0
* 字符型(char): 0或者'\u0000'
* 布尔型(boolean):false
*
* 引用数据类型(类,数组,接口): null
*
* 局部变量:
* 没有默认值,意味着我们在调用局部变量之前,一定要显式赋值
* 特别地:形参在调用时,我们赋值即可
*
* 2.4 内存中加载的位置
* 属性:加载到堆空间中(非static)
* 局部变量: 加载到栈空间中
*
*
*/
public class UserTest {
public static void main(String[] args) {
User user1 = new User();
System.out.println(user1.name);//null
System.out.println(user1.age);//0
System.out.println(user1.isMale);//false
user1.speak("CN");
}
}
class User {
//属性(成员变量)
String name;
int age;
boolean isMale;
public void speak(String lang) { //lang局部变量:形参
System.out.println("开发时使用"+lang+"语言");
}
public void eat() {
String fruit = "樱桃"; //局部变量,只在当前方法内有效
System.out.println("未曾对"+fruit+"感兴趣");
}
}
三、类中方法的声明和使用
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/1/26
* @Description
* @Version 1.0
*/
/**
* 类中方法的声明和使用
*
* 1.方法的声明:权限修饰符 返回值类型 方法名(形参列表){
* 方法体
* }
*
* 2.说明:
* 2.1 关于权限修饰符,默认方法的权限访问修饰符都先使用public
* public private 缺省 protected
*
* 2.2 返回值类型,有返回值 vs 没有返回值
* 2.2.1 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时方法中
* 需要使用return关键字来返回指定类型d的变量或者常量
* 如果方法没有返回值,则声明方法时,使用void来表示
*
* 2.3 方法名 标识符,遵循标识符的规则和规范,“见名知意”
* 2.4 形参列表:方法可以声明0个,1个或者多个形参
* 格式:数据类型1 形参1,数据类型2 形参数2.....
*
* 2.5 return关键字
* 使用范围:使用在方法体中
* 作用:1)结束方法
* 2)针对有返回值的方法,使用return返回数据
*
* 3.方法的使用,可以调用当前类的属性和方法
* 特殊地:方法A中又调用了方法A:递归调用
*
* 方法中,不可以定义方法
*
*
*/
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
System.out.println(customer.getName());
}
}
class Customer{
//属性
String name;
int age;
//方法,没有返回值
public void eat() {
System.out.println("客户也需吃东西");
}
//方法,有返回值
public String getName() {
eat();
return "卢璐";
}
}
四、类和对象最基本的demo
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/1/26
* @Description 类和对象的最基本使用
* @Version 1.0
*/
public class PersonTest {
public static void main(String[] args) {
/**
* 类和对象的最基本使用
*/
//创建一个对象,通过new类名()
Person p1 = new Person();
//设置或者调用类的属性
p1.name = "小名";
p1.isMale = true;
System.out.println("name为:"+p1.name); // 小名
System.out.println("是否为男生:" + p1.isMale);//true
//调用类的方法
p1.eat();
p1.sing();
p1.jump(p1.name);
//再new一个p2
Person p2 = new Person();
System.out.println(p2.name);//null
System.out.println(p2.isMale);//false(默认值)
//赋值操作,相当于将p1的地址赋值给p3,所以p1和p3在堆空间所指向的是同一个对象实体
Person p3 = p1;
System.out.println(p3.name);//小名
p3.age = 10;
System.out.println(p1.age);//10
}
}
class Person{
String name;
int age = 18;
boolean isMale;
public void eat() {
System.out.println("eat...");
}
public void sing() {
System.out.println("sing...");
}
public void jump(String name) {
System.out.println(name+ "jump");
}
}
五、匿名对象的使用
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/12
* @Description 匿名对象的使用
* @Version 1.0
*/
public class InstanceTest {
public static void main(String[] args) {
/**
* 匿名对象:
* 1.理解:创建的对象,没有显式的赋给一个变量名,即为匿名对象
* 2.特征:匿名内部类,只能调用一次
* 3.使用:
*/
Phone p = new Phone();
System.out.println(p); // 输出地址值,引用数据类型存的值要么是null要么是地址值
p.sendEmail();
p.playGame();
// 匿名对象
new Phone().sendEmail();
new Phone().playGame();
new Phone().price = 998;
new Phone().printPrice(); // 0.0
// 匿名对象的使用
PhoneMall phoneMall = new PhoneMall();
phoneMall.show(new Phone());
}
}
class PhoneMall{
public void show(Phone phone) {
phone.sendEmail();;
phone.playGame();
}
}
class Phone{
double price; //价格
public void sendEmail() {
System.out.println("发送邮件!!!");
}
public void playGame() {
System.out.println("打游戏!!!");
}
public void printPrice() {
System.out.println("价格为:" + price);
}
}
六、构造器
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/15
* @Description 构造器
* @Version 1.0
*/
public class DogTest {
public static void main(String[] args) {
/**
* 一。构造器的作用:
* 创建对象
*
* 二。说明:
* 1.如果没有显式提供一个构造器的话,系统默认会提供一个空参的构造器
* 2.定义构造器的格式:权限修饰符 类名(形参列表){}
* 3.一个类中定义的多个构造器,构成重载
* 4.一旦我们显式地定义了类的构造器之后,系统就不再提供默认的空参构造器
* 5.一个类中,至少会有一个构造器
*/
Dog dog = new Dog();
dog.eat();
Dog dog1 = new Dog("小白");
System.out.println(dog1.name);
Dog dog2 = new Dog("小黑",3);
System.out.println(dog2.name);
System.out.println(dog2.age);
}
}
class Dog{
String name;
int age;
public Dog(){
System.out.println("无参构造器。。。");
}
public Dog(String n) {
name = n;
}
public Dog(String n, int a) {
name = n;
age = a;
}
public void eat() {
System.out.println("eat...");
}
public void say() {
System.out.println("say...");
}
}
七、this关键字
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/16
* @Description this关键字
* @Version 1.0
*/
public class EegTest {
public static void main(String[] args) {
/**
* 1.this可以用来修饰,调用:属性,方法,构造器
*
* 2.this修饰属性或方法
* this:理解为当前对象
*
* 在类的方法中,我们可以使用this.方法或者this.属性的方式,来调用当前对象属性或方法,但是,通常情况下,我们都选择省略this
* 除非,在特殊情况下,如果方法的形参,和属性的名称相同时,我们必须使用this.属性名来表明此变量为属性而非形参
*
* 3.this调用构造器
* 1)我们在类的构造器中,可以显式的使用“this(形参列表)”方式,来调用当前类中指定的其他构造器
* 2)构造器中,不能使用“this(形参列表)”方式来调自己
* 3)如果一个类中有n个构造器,那么此类中最多有n-1个构造器中可以使用“this(形参列表)”方式来调用其他构造方法
* 4)规定:“this(形参列表)”,必须放在首行
* 5)构造器内部,最多只能声明一个“this(形参列表)”,来调用其它构造器
*
*/
Eeg eeg = new Eeg();
eeg.setAge(11);
System.out.println(eeg.getAge());
eeg.eat();
Eeg eeg1 = new Eeg("xiao",11);
System.out.println(eeg1.getAge()); // 11
}
}
class Eeg{
private int age;
private String name;
public Eeg() {
System.out.println("初始化数据");
}
public Eeg(int age) {
this();
this.age = age;
}
public Eeg(String name, int age) {
this(age);
this.name = name;
// this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void eat() {
System.out.println("eat..");
this.say();
}
public void say() {
System.out.println("say...");
}
}
八、封装
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/15
* @Description 封装
* @Version 1.0
*/
public class AnimalTest {
public static void main(String[] args) {
/**
* 面向对象的特征一:封装
* 封装性的体现:
* 我们将类的属性xxx私有化(private),同时提供公共的get方法获取属性的值,以及set方法来设置属性的值
*
* 拓展:不对外暴露的私有方法,单例模式等
*
* 封装性的体现需要权限修饰符来配合
* 1.java规定的4种权限(从小到大排列):private,缺省(default),protected,public
* 修饰符 类内部 同一个包 不同包的子类 同一个工程
* private yes
* default yes yes
* protected yes yes yes
* public yes yes yes yes
*
* 2.4种权限修饰符可以用来修饰类及类的内部结构,属性,方法,构造器,内部类
* 注意:修饰类的话,只能使用:缺省,public
*/
Animal animal = new Animal();
animal.age = 11;
animal.name = "小白";
// animal.legs = 4;
animal.eat();
animal.show();
}
}
class Animal{
int age;
String name;
private int legs;
public void setLegs(int l) {
if (l >= 0 && l % 2 == 0) {
legs = 1;
} else {
legs = 0;
}
}
public int getLegs() {
return legs;
}
public void eat() {
System.out.println("eat.....");
}
public void show() {
System.out.println("name="+name+",age="+age+",legs="+legs);
}
}
九、属性赋值的顺序
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/16
* @Description 属性赋值的顺序
* @Version 1.0
*/
public class assignmentTest {
public static void main(String[] args) {
/**
* 属性赋值的先后顺序
*
* 1.默认初始化
* 2.显式初始化
* 3.构造器
* 4.对象.方法或者对象.属性的方式赋值
*/
Fruit f = new Fruit();
f.setAge(3);
System.out.println(f.age);
}
}
class Fruit{
int age = 1;
String name;
public Fruit() {
age = 2;
}
public void setAge(int a) {
age = a;
}
}
十、可变参数
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/12
* @Description 可变参数
* @Version 1.0
*/
public class MethodArgTest {
public static void main(String[] args) {
/**
* 1.jdk:1.5新增内容,可变参数
* 2.具体使用
* 2.1 格式: 数据类型 ... 变量名
* 2.2 当调用可变参数的方法时,传入的参数可以是,0,1,2...个
* 2.3 可变个数形参在方法的形参中,只能放在最后
* 2.4 可变个数形参在方法的形参中,只能声明一个
* 2.5 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载
*
*/
MethodArgTest test = new MethodArgTest();
test.show("haha");// showS
test.show("a","b","c"); // showArgs
test.show(); //showArgs
}
public void show(int i) {
}
public void show(String s) {
System.out.println("showS");
}
public void show(String ... args) {
System.out.println("showArgs");
for (String s : args) {
System.out.println(s);
}
}
// 报错,不能同时存在
// public void show(String []args) {
// System.out.println("showArgs");
// for (String s : args) {
// System.out.println(s);
// }
// }
}
十一、方法重载
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/12
* @Description 方法重载
* @Version 1.0
*/
public class OverLoadTest {
public static void main(String[] args) {
/**
* 方法的重载
* 1.定义:在一个类中,定义了多个方法名一样,但是参数列表不一致的方法,与方法返回值无关
*
* “两同一不同”,即同一个类,同一个方法名,不同的是参数列表,参数类型等
*
* 如何调用某一个指定的方法,根据传入的参数的个数和数据类型来区分。
* */
OverLoadTest test = new OverLoadTest();
test.getNum("1","2"); // d
}
public void getNum(int i, int j) {
System.out.println("a");
}
public void getNum(int i, String j) {
System.out.println("b");
}
public void getNum(String i, int j) {
System.out.println("c");
}
public void getNum(String i, String j) {
System.out.println("d");
}
}
十二、递归方法(了解)
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/15
* @Description 递归方法(了解)
* @Version 1.0
*/
public class RecursionTest {
public static void main(String[] args) {
/**
* 1.递归方法: 一个方法体内调用它自身
* 2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无需循环控制
* 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
*/
// 100之内的自然数相加
// int sum = 0;
// for (int i = 1; i <= 100; i++) {
// sum += i;
// }
// 用递归
RecursionTest test = new RecursionTest();
int sum = test.getSum(3);
System.out.println(sum);
int r = test.fun1(10);
System.out.println(r);
}
//求1到n的和
public int getSum(int number) {
if (number == 1) {
return 1;
} else {
return number + getSum(number - 1);
}
}
//求1到n的阶乘
public int getSum1(int n) {
if (n == 1) {
return 1;
} else {
return n * getSum(n - 1);
}
}
//练习,已知有一个数列,f(0)=1,f(1)=4,f(n+2) = 2*f(n+1) + f(n)
//其中,n是大于0的整数,求f(10)的值
public int fun1(int n) {
if (n == 0) {
return 1;
} else if (n == 1) {
return 4;
} else {
// 将f(n+2)当作f(n)即可
return 2*(fun1(n-1)) + fun1(n-2);
}
}
}
十三、值传递机制
1.
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/12
* @Description
* @Version 1.0
*/
public class ValueTransferTest1 {
public static void main(String[] args) {
/**
* 方法的形参的传递机制:值传递
* 1.形参:方法定义时,小括号里的参数
* 实参:调用方法时,实际传递给形参的数据
*
*
* 2.值传递机制:
* 如果参数为基本数据类型,此时实参赋值给形参的是实参真实的数据值
* 如果参数为引用数据类型,此时赋值的是变量所保存的数据的地址值
*/
int m = 10;
int n = 20;
System.out.println("m="+m+",n="+n);//m=10,n=20
// 交换两个变量的值
// int temp = m;
// m = n;
// n = temp;
// System.out.println("m="+m+"n=" + n);m=20,n=10
ValueTransferTest1 test = new ValueTransferTest1();
test.swap(m,n);
System.out.println("m="+m+",n="+n);//m=10,n=20
}
private void swap(int m, int n) {
int temp = m;
m = n;
n = temp;
System.out.println("m="+m+"n=" + n);//m=20,n=10
}
}
package com.yl.pdfdemo.day03;
/**
* @Author wfj
* @Date 2021/3/12
* @Description
* @Version 1.0
*/
public class ValueTransferTest2 {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m" + data.m +",n="+data.n);//m=10,n=20
ValueTransferTest2 test = new ValueTransferTest2();
test.swap(data);
System.out.println("m" + data.m +",n="+data.n);//m=20,n=10
}
private void swap(Data data) {
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data {
int m;
int n;
}
3.基本数据类型,值传递内存结构图
4.引用数据类型,值传递内存结构图
十四、一些面向对象相关的练习
1.
package com.yl.pdfdemo.day03.test;
/**
* @Author wfj
* @Date 2021/1/27
* @Description
* @Version 1.0
*/
public class Circle {
//属性
double radius;
//求园的面积
public double findArea() {
double area = Math.PI * radius * radius;
return area;
}
}
package com.yl.pdfdemo.day03.test;
/**
* @Author wfj
* @Date 2021/1/27
* @Description
* @Version 1.0
*/
public class CircleTest {
public static void main(String[] args) {
Circle circle = new Circle();
circle.radius= 5;
System.out.println(circle.findArea());
}
}
package com.yl.pdfdemo.day03.test;
/**
* @Author wfj
* @Date 2021/1/26
* @Description
* @Version 1.0
*/
public class Person {
String name;
int age;
int sex;
public void study() {
System.out.println("study.....");
}
public void showAge() {
System.out.println("age:" + age);
}
public int addAge(int i) {
age += i;
return age;
}
}
package com.yl.pdfdemo.day03.test;
/**
* @Author wfj
* @Date 2021/1/26
* @Description
* @Version 1.0
*/
public class PersonTest {
public static void main(String[] args) {
/**
* 注意:每new一个对象,其都会在堆空间开辟一块空间,每个对象的属性,方法都是对应自己的,并不会冲突!!!
*/
Person p1 = new Person();
//设置属性值
p1.name = "兰溪";
p1.age = 18;
p1.sex = 1;
//调用方法
p1.study();
p1.showAge();
int nAge = p1.addAge(3);
System.out.println(nAge);//21
System.out.println(p1.age);//21
Person p2 = new Person();
p2.showAge();//0
}
}
package com.yl.pdfdemo.day03.test;
/**
* @Author wfj
* @Date 2021/1/27
* @Description 对象数组
* @Version 1.0
*/
public class StudentTest {
public static void main(String[] args) {
Student[] stus = new Student[20];
for (int i = 0; i < stus.length; i++) {
//给数组元素赋值
stus[i] = new Student();
//学号
stus[i].num = i+1;
//年级[1-6] Math.random()返回的是double类型的值,范围[0.0-1.0)
stus[i].state = (int)(Math.random() * 6) + 1;
//成绩[1-100]
stus[i].score = (int)(Math.random() * 100) + 1;
}
//遍历年级为3的
for (int i = 0 ; i < stus.length; i++) {
if (stus[i].state == 3) {
System.out.println(stus[i]);
}
}
//根据成绩排序
for (int i = 0; i < stus.length - 1; i++) {
for (int j = 0 ; j < stus.length - 1 - i; j++) {
if (stus[j].score > stus[j+1].score) {
Student temp = new Student();
stus[j] = stus[j+1];
stus[j+1] = temp;
}
}
}
}
}
class Student{
int num;
int state;
int score;
}