面向对象
引入问题:
单独变量解决 => 不利于数据的管理
数组 => (1)数据类型无法体现 (2)只能通过下标获取信息,造成变量名和内容对应关系不明确 (3)不能体现行为
类与对象
类:自定义的数据类型,(属性,行为)
对象:一个具体的实例
对象内存分布
引用数据类型放在常量池
属性/成员变量(field)
可以是基本数据类型,也可以是引用类型
1) 访问修饰符:控制属性的访问范围,四种(public private protected 默认)
2) 属性的定义类型可以为任意类型
3) 属性如果不赋值,则默认值和数组相同
创建对象
- 先声明在创建
Cat cat;
cat = new Cat();
- 直接创建
Cat cat = new Cat();
类和对象的内存分配机制
本质是地址引用
java创建对象的流程简单分析
- 先加载Person类信息(属性和方法信息,只会加载一次)
- 在堆中分配空间,进行默认初始化,
- 把地址赋给 p,p 就指向对象
- 进行指定初始化,p.name = "jack" p.age = 10
成员方法(简称方法)
定义
public 返回类型 方法名 (形参列表...){ //方法体
语句;
return 返回值;
}
例子:
public class Method01 {
public static void main(String[] args) {
Person aPerson = new Person();
// 方法写好后,不调用不会输出
aPerson.speak(); // 调用方法
System.out.println(aPerson.cal01());
System.out.println(aPerson.cal02(20));
System.out.println(aPerson.getSum(13, 17));
}
}
class Person {
String name;
int age;
// public 表示方法公开, void 表示没有返回值
// speak() speak 是方法名, ()形参列表
public void speak(){
System.out.println("I\'m a good man.");
}
// 计算 1 + 2 + ... + 1000 的结果
public int cal01() {
int res = 0;
for (int i = 1; i < 1001; i++) {
res += i;
}
return res;
}
// (int n) 形参列表,表示当前有一个形参 n, 可以接受用户输入
public int cal02(int n){
int res = 0;
for (int i = 1; i < n + 1; i++) {
res += i;
}
return res;
}
public int getSum(int num1, int num2){
int res = num1 + num2;
return res;
}
}
方法调用内存分析
方法调用小结
- 当程序执行到方法时,会开辟出一个独立的空间(栈空间)
- 当方法执行完毕,或者执行到 return 语句时,就会返回
- 返回到调用方法的地方
- 返回后,继续执行方法后面的代码
- 当 main 方法(栈)执行完毕,这个程序退出
成员方法的好处
- 提高代码的复用性
- 可以将实现的细节封装起来,然后供其他用户来调用即可
细节
返回数据类型
- 一个方法最多有一个返回类型(如果返回多个结果,返回数组)
- 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
- 如果方法有返回数据类型,则方法体中最后执行的语句必须为 return 值;而且返回要求返回值类型必须和 return 的值类型一致或兼容
- 如果方法是void,则方法体中可以没有 return,或者只写 return ;
- 方法名遵循驼峰命名法,如 getSum
参数列表
一个方法可以有 0 个参数,也可以有多个参数,中间用逗号隔开,例如 :
getSum(int n1, int n2)
参数类型可以为任意类型,包含基本类型和引用类型,比如 :
printArr(int[][] map)
- 调用带参数的方法时,一定对应这参数列表传入相同类型或兼容类型的参数。
- 方法定义时的参数称为形式参数,简称形参;方法调用时的参数成为实际参数,简称实参,实参和形参的类型要一致或兼容,个数、顺序必须一致。
成员方法
- 同一个类中的方法调用:可以直接调用。
- 跨类的方法 A 类调用 B 类方法:需要先创建 B 对象,再通过对象名调用。对象名.方法名(参数)
- 访问修饰符,后面再细说。
练习
public class MethodExercise01 {
public static void main(String[] args) {
AA j = new AA();
System.out.println(j.isOdd(3));
j.print(3, 4, '#');
}
}
class AA{
// 判断奇数还是偶数,奇数odd返回 true,偶数返回 false
public boolean isOdd(int inputNum){
return inputNum % 2 == 0 ? false : true;
}
public void print(int row, int column, char c){
for(int i = 0; i < row; i++){
for (int j = 0; j < column; j++) {
System.out.print(c + " ");
}
System.out.println();
}
}
}
成员方法传参机制
基本数据类型
public class MethodParameter01{
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("main中交换前:");
System.out.println("a = " + a + " b = " + b);
AA obj = new AA();
obj.swap(a, b);
System.out.println("main中交换后:");
System.out.println("a = " + a + " b = " + b);
}
}
class AA{
public void swap(int a, int b){
System.out.println("swap中交换前:");
System.out.println("a = " + a + " b = " + b);
int tmp = a;
a = b;
b = tmp;
System.out.println("swap中交换后:");
System.out.println("a = " + a + " b = " + b);
}
}
引用数据类型
public class MethodParameter02{
public static void main(String[] args) {
int[] arr = {1, 2, 3};
B b = new B();
b.test(arr);
System.out.println("main中数组:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
class B{
public void test(int[] arr){
arr[0] = 200;
System.out.println("test中数组:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
引用类型传递的是地址(也是值,但是值是地址),可以通过形参影响实参
置空时注意是哪一个栈置空,对象指向指控并不影响对象属性。
深刻理解内存图,基本数据类型传递,引用传递
练习
public class MethodExercise02 {
public static void main(String[] args) {
Person p = new Person();
p.name = "Jack";
p.age = 20;
MyTools tools = new MyTools();
Person p2 = tools.copyPerson(p);
System.out.println("p: " + p.name + " is "
+ p.age + " years old.");
System.out.println("p2: " + p2.name + " is "
+ p2.age + " years old.");
// p 和 p2 是 Person 对象,但是两个对象独立,属性相同
// 可以通过 输出对象的 hashCode 看对象是否是一个
System.out.println("p的hashCode: " + p.hashCode());
System.out.println("p2的hashCode: " + p2.hashCode());
System.out.println(p == p2);
}
}
class Person {
String name;
int age;
}
class MyTools {
public Person copyPerson (Person p){
Person p2 = new Person();
p2.name = p.name; // 名字赋值
p2.age = p.age; // 年龄赋值
// 克隆对象
return p2;
}
}
递归(recursion)
递归调用机制
public class recursion01 {
public static void main(String[] args) {
T t1 = new T();
t1.test(4);
}
}
class T {
public void test(int n){
if(n > 2){
test(n - 1);
}
System.out.println("n = " + n);
}
}
结果:
加一个else:
public class recursion01 {
public static void main(String[] args) {
T t1 = new T();
t1.test(4);
}
}
class T {
public void test(int n){
if(n > 2){
test(n - 1);
}
else{
System.out.println("n = " + n);
}
}
}
输出:
==》每一个栈完整执行方法
public class recursion01 {
public static void main(String[] args) {
T t1 = new T();
System.out.println(t1.factorial(5));
}
}
class T {
// factorial => 阶乘
public int factorial(int n){
if(n == 1){
return 1;
}
else{
return n * factorial(n - 1);
}
}
}
输出
递归细节
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的,不会相互影响,比如n变量
- 如果方法中使用的是引用变量(数组,对象),就会共享该引用类型的数据
- 递归必须向推出递归的条件逼近,否则就是无限递归,出现StackOverflowError
- 当一个方法执行完毕,或遇到return,就会返回遵循谁调用就将结果返回给谁,同时,当方法执行完毕或者返回时,该方法也就执行完毕
public class RecursionExercise01 {
public static void main(String[] args) {
// 桃子问题
Fib f = new Fib();
int day = 1;
int peachNum = f.peach(day);
if(peachNum != -1){
System.out.println("第 " + day + " 天有 " + peachNum
+ " 个桃子!");
}
}
}
class Fib{
public int peach(int day){
if(day == 10){ // 第十天只有一个桃子
return 1;
}else if( day >= 1 && day <= 9){
return ((peach(day + 1) + 1) * 2);
}else{
System.out.println("day在1-10");
return -1;
}
}
}
评论