类与对象
类和对象的区别和分割
- 类是形象的,概念的,代表一类事物, 比方人类, 猫类.., 即它是数据类型.
- 对象是具体的,理论的,代表一个具体事物, 即是实例.
- 类是对象的模板,对象是类的一个个体,对应一个实例
对象在内存中存在模式!
字符串实质上是一个援用类型,依照 jvm 的规定会把字符串放在办法区的常量池两头。
栈中的是对象援用(对象名),实际上的对象在堆中。
// 创立 Person 对象
// p1 是对象名(对象援用)
// new Person() 创立的对象空间(数据) 才是真正的对象
Person p1 = new Person();
// 对象的属性默认值,恪守数组规定:
属性 / 成员变量 / 字段
从概念或叫法上看:成员变量 = 属性 = field(字段)(即成员变量是用来示意属性的,对立叫属性)
class Car {
String name;// 属性, 成员变量, 字段 field
double price;
String color;
String[] master;// 属性能够是根本数据类型,也能够是援用类型(对象,数组)
}
属性是类的一个组成部分,个别是根本数据类型, 也可是援用类型(对象,数组)。比方后面定义猫类的 int age 就是属性
注意事项和细节阐明
- 属性的定义语法同变量,示例:拜访修饰符属性类型属性名;
拜访修饰符:管制属性的拜访范畴
有四种拜访修饰符 public, proctected, 默认, private , 前面我会具体介绍 - 属性如果不赋值,有默认值,规定和数组统一。
如何创建对象
- 先申明再创立
Cat cat ; // 申明对象 cat
cat = new Cat(); // 创立 - 间接创立
Cat cat = new Cat();
如何拜访属性
根本语法
对象名. 属性名;
cat.name ;
cat.age;
cat.color;
Person p1=new Person0;
p1.age=10;
p1.name="小明";
Person p2=p1;// 把 p1 赋给了 p2,让 p2 指向 p1
System.out.println(p2.age);
请问:p2.age 到底是多少? 10 并画出内存图:
外围:援用传递传递的是地址。
成员办法
在某些状况下,咱们要须要定义成员办法(简称办法)。
办法的调用机制原理!
成员办法的益处
- 进步代码的复用性
- 能够将实现的细节封装起来,而后供其余用户来调用即可
成员办法的定义
拜访修饰符 返回数据类型 办法名(形参列表..){// 办法体
语句;
return 返回值;
}
// 如果办法是 void,则办法体中能够没有 return 语句,或者只写 return;
拜访修饰符 (作用是管制办法应用的范畴)
如果不写默认拜访,[有四种: public, protected, 默认, private]
办法不能嵌套定义!
成员办法传参机制
根本数据类型,传递的是值(值拷贝),形参的任何扭转不影响实参!
援用数据类型的传参机制
援用类型传递的是地址(传递也是值,然而值是地址),能够通过形参影响实参!
栈的值是地址,改的时候批改的是对应堆中的值。
例子:
public class MethodParameter02 {
// 编写一个 main 办法
public static void main(String[] args) {
// 测试
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");
// }
// System.out.println();
// 测试
Person p = new Person();
p.name = "jack";
p.age = 10;
b.test200(p);
// 测试题, 如果 test200 执行的是 p = null , 上面的后果是 10
// 测试题, 如果 test200 执行的是 p = new Person();..., 上面输入的是 10
System.out.println("main 的 p.age=" + p.age);//10000
}
}
class Person {
String name;
int age;
}
class B {public void test200(Person p) {
//p.age = 10000; // 批改对象属性
// 思考
p = new Person();
p.name = "tom";
p.age = 99;
// 思考
//p = null;
}
// B 类中编写一个办法 test100,// 能够接管一个数组,在办法中批改该数组,看看原来的数组是否变动
public void test100(int[] arr) {arr[0] = 200;// 批改元素
// 遍历数组
System.out.println("test100 的 arr 数组");
for(int i = 0; i < arr.length; i++) {System.out.print(arr[i] + "\t");
}
System.out.println();}
}
B 类中编写一个办法 test100,能够接管一个数组,在办法中批改该数组,看看原来的数组是否变动?会变动
B 类中编写一个办法 test200,能够接管一个 Person(age,sal)对象,在办法中批改该对象属性,看看原来的对象是否变动?会变动.
p=null 和 p = new Person(); 对应示意图
这里再对 class B 中的 p 进行批改,因为在 Class B 中从新 new 了一个 p,因而 p 的指针产生了扭转,指向堆中的一个新空间,因而这时批改 p 的参数,不对 main 中对象造成影响。
成员办法返回类型是援用类型利用实例
通过这种形式能够编写办法复制对象。
public class MethodExercise02 {
// 编写一个 main 办法
public static void main(String[] args) {Person p = new Person();
p.name = "milan";
p.age = 100;
// 创立 tools
MyTools tools = new MyTools();
Person p2 = tools.copyPerson(p);
// 到此 p 和 p2 是 Person 对象,然而是两个独立的对象,属性雷同
System.out.println("p 的属性 age=" + p.age + "名字 =" + p.name);
System.out.println("p2 的属性 age=" + p2.age + "名字 =" + p2.name);
// 这里老师提醒:能够同 对象比拟看看是否为同一个对象
System.out.println(p == p2);//false
}
}
class Person {
String name;
int age;
}
class MyTools {
// 编写一个办法 copyPerson,能够复制一个 Person 对象,返回复制的对象。克隆对象,// 留神要求失去新对象和原来的对象是两个独立的对象,只是他们的属性雷同
//
// 编写办法的思路
//1. 办法的返回类型 Person
//2. 办法的名字 copyPerson
//3. 办法的形参 (Person p)
//4. 办法体, 创立一个新对象,并复制属性,返回即可
public Person copyPerson(Person p) {
// 创立一个新的对象
Person p2 = new Person();
p2.name = p.name; // 把原来对象的名字赋给 p2.name
p2.age = p.age; // 把原来对象的年龄赋给 p2.age
return p2;
}
}
办法递归调用
办法递归调用
列举两个小案例, 来帮忙大家了解递归调用机制
- 打印问题
- 阶乘问题
public class Recursion01 {
// 编写一个 main 办法
public static void main(String[] args) {T t1 = new T();
t1.test(4);// 输入什么?n=2 n=3 n=4
int res = t1.factorial(5);
System.out.println("5 的阶乘 res =" + res);
}
}
class T {
// 剖析
public void test(int n) {if (n > 2) {test(n - 1);
}
System.out.println("n=" + n);
}
//factorial 阶乘
public int factorial(int n) {if (n == 1) {return 1;} else {return factorial(n - 1) * n;
}
}
}
递归重要规定
1. 执行一个办法时,就创立一个新的受爱护的独立空间(钱空间)
2. 办法的局部变量是独立的, 不会相互影响, 比方 n 变量
3. 如果办法中应用的是援用类型变量 (比方数组,对象),就会共享该引
用类型的数据。
4. 递归必须向退出递归的条件迫近, 否则就是有限递归, 呈现 StackOverflowError
5. 当一个办法执行结束,或者遇到 return,就会返回,恪守谁调用,就
将后果返回给谁,同时当办法执行结束或者返回时,该办法也就执行结束。
递归调用利用实例 - 汉诺塔
public class HanoiTower {
// 编写一个 main 办法
public static void main(String[] args) {Tower tower = new Tower();
tower.move(64, 'A', 'B', 'C');
}
}
class Tower {
// 办法
//num 示意要挪动的个数, a, b, c 别离示意 A 塔,B 塔, C 塔
public void move(int num , char a, char b ,char c) {
// 如果只有一个盘 num = 1
if(num == 1) {System.out.println(a + "->" + c);
} else {// 如果有多个盘,能够看成两个 , 最上面的和下面的所有盘(num-1)
//(1)先挪动下面所有的盘到 b, 借助 c
move(num - 1 , a, c, b);
//(2)把最上面的这个盘,挪动到 c
System.out.println(a + "->" + c);
//(3)再把 b 塔的所有盘,挪动到 c , 借助 a
move(num - 1, b, a, c);
}
}
}
递归调用利用实例 - 八皇后问题
八皇后问题,是一个古老而驰名的问题,是回溯算法的典型案例。该问题是国内西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
- 第一个皇后先放第一行第一列
- 第二个皇后放在第二行第一列、而后判断是否 OK,如果不 OK,持续放在第二列、第三列、顺次把所有列都放完,找到一个适合
- 持续第三个皇后, 还是第一列、第二列 ……. 直到第 8 个皇后也能放在一个不抵触的地位, 算是找到了一个正确解
- 当失去一个正确解时, 在栈回退到上一个栈时,就会开始回溯,行将第一个皇后,放到第一列的所有正确解, 全副失去.
- 而后回头持续第一个皇后放第二列,前面持续循环执行 1,2,3.4 的步骤
- 阐明: 实践上应该创立一个二维数组来示意棋盘,然而实际上能够通过算法,用一个一维数组即可解决问题. arr[8]={0,4,7,5.2, 6,1.3)// 对应 arr 下标示意第几行,即第几个皇后,arr[i]= val , val 示意第 i + 1 个皇后,放在第 i + 1 行的第 val+ 1 列
办法重载(OverLoad)
根本介绍
java 中容许同一个类中,多个同名办法的存在,但要求形参列表不统一!
重载的益处
- 加重了起名的麻烦
- 加重了记名的麻烦
注意事项和应用细节
1)办法名: 必须雷同
2)形参列表: 必须不同(形参类型或个数或程序,至多有一样不同,参数名无要求)
3)返回类型: 无要求
可变参数
基本概念
java 容许将同一个类中多个同名同性能但参数个数不同的办法,封装成一个办法。就能够通过可变参数实现
根本语法
拜访修饰符返回类型办法名(数据类型... 形参名) {}
看一个案例类 HspMethod,办法 sum。
public class VarParameter01 {
// 编写一个 main 办法
public static void main(String[] args) {HspMethod m = new HspMethod();
System.out.println(m.sum(1, 5, 100)); //106
System.out.println(m.sum(1,19)); //20
}
}
class HspMethod {
// 能够计算 2 个数的和,3 个数的和,4. 5,。。// 能够应用办法重载
// public int sum(int n1, int n2) {// 2 个数的和
// return n1 + n2;
// }
// public int sum(int n1, int n2, int n3) {// 3 个数的和
// return n1 + n2 + n3;
// }
// public int sum(int n1, int n2, int n3, int n4) {// 4 个数的和
// return n1 + n2 + n3 + n4;
// }
//.....
// 下面的三个办法名称雷同,性能雷同, 参数个数不同 -> 应用可变参数优化
// 老韩解读
//1. int... 示意承受的是可变参数,类型是 int , 即能够接管多个 int(0- 多)
//2. 应用可变参数时,能够当做数组来应用 即 nums 能够当做数组
//3. 遍历 nums 求和即可
public int sum(int... nums) {//System.out.println("接管的参数个数 =" + nums.length);
int res = 0;
for(int i = 0; i < nums.length; i++) {res += nums[i];
}
return res;
}
}
注意事项和应用细节
1)可变参数的实参能够为 0 个或任意多个。
2)可变参数的实参能够为数组。
3)可变参数的实质就是数组。
4)可变参数能够和一般类型的参数一起放在形参列表,但必须 保障可变参数在最初。
5)一个形参列表中只能呈现一个可变参数。
public void f3(int... nums1, double... nums2)(X 谬误)
作用域
根本应用
1. 在 java 编程中,次要的变量就是 属性 (成员变量) 和 局部变量。
2. 咱们说的 局部变量个别是指在成员办法中定义的变量。
3.java 中作用域的分类
全局变量:也就是属性,作用域为整个类体(Cat 类:cry eat 等办法应用属性)
局部变量:也就是除了属性之外的其余变量,作用域为定义它的代码块中!
4. 全局变量 (属性) 能够不赋值,间接应用,因为有默认值,局部变量必须赋值后,
能力应用,因为没有默认值。
public class VarScope {
// 编写一个 main 办法
public static void main(String[] args) {}}
class Cat {
// 全局变量:也就是属性,作用域为整个类体 Cat 类:cry eat 等办法应用属性
// 属性在定义时,能够间接赋值
int age = 10; // 指定的值是 10
// 全局变量 (属性) 能够不赋值,间接应用,因为有默认值,double weight; // 默认值是 0.0
public void hi() {
// 局部变量必须赋值后,能力应用,因为没有默认值
int num = 1;
String address = "北京的猫";
System.out.println("num=" + num);
System.out.println("address=" + address);
System.out.println("weight=" + weight);// 属性
}
public void cry() {
//1. 局部变量个别是指在成员办法中定义的变量
//2. n 和 name 就是局部变量
//3. n 和 name 的作用域在 cry 办法中
int n = 10;
String name = "jack";
System.out.println("在 cry 中应用属性 age=" + age);
}
public void eat() {System.out.println("在 eat 中应用属性 age=" + age);
//System.out.println("在 eat 中应用 cry 的变量 name=" + name);// 谬误
}
}
1. 属性和局部变量能够重名, 拜访时遵循就近准则。
2. 在同一个作用域中,比方在同一个成员办法中,两个局部变量,不能重名。
3. 属性生命周期较长,随同着对象的创立而创立,随同着对象的销毁而销毁。部分变
量,生命周期较短,随同着它的代码块的执行而创立,随同着代码块的完结而销毁。
4. 作用域范畴不同
全局变量 / 属性:能够被本类应用,或其余类应用(通过对象调用)
局部变量: 只能在本类中对应的办法中应用
5. 修饰符不同
全局变量 / 属性能够加修饰符
局部变量不能够加修饰符
构造方法 / 结构器
[修饰符] 办法名(形参列表){办法体;}
- 结构器的修饰符能够默认,也能够是 public protected private
- 结构器没有返回值
- 办法名和类名字必须一样
- 参数列表和成员办法一样的规定
- 结构器的调用, 由 零碎实现
根本介绍
构造方法又叫结构器(constructor),是类的一种非凡的办法,它的次要作用是实现对新对象的初始化。它有几个特点:
- 办法名和类名 雷同
- 没有返回值
- 在创建对象时,零碎会主动的调用该类的结构器实现对象的初始化。
注意事项和应用细节
1. 一个类能够定义多个不同的结构器, 即结构器重载
比方:咱们能够再给 Person 类定义一个结构器, 用来创建对象的时候, 只指定人名, 不须要指定年龄。
2. 结构器名和类名要雷同
3. 结构器没有返回值
4.结构器是实现对象的初始化, 并不是创建对象
5. 在创建对象时,零碎主动的调用该类的构造方法
6. 如果程序员没有定义结构器,零碎会主动给类生成一个默认无参结构器 (也
叫默认结构器). 比方 Dog(){}, 应用 javap 指令反编译看看
能够应用 javap Dog.class 查看
7. 一旦定义了本人的结构器, 默认的结构器就笼罩了,就不能再应用默认的无
参结构器,除非显式的定义一下, 即:Dog(){}
javap 的应用
javap 是 JDK 提供的一个命令行工具,javap 能对给定的 class 文件提供的字节代码进行反编译。通过它,能够对照源代码和字节码,从而理解很多编译器外部的工作。
应用格局
javap <options> <classes>
罕用
javap -c -v 类名
-help --help -? 输入此用法音讯
-version 版本信息
-v -verbose 输入附加信息
-l 输入行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受爱护的 / 公共类和成员
-package 显示程序包 / 受爱护的 / 公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输入外部类型签名
-sysinfo 显示正在解决的类的
零碎信息 (门路, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的地位
-cp <path> 指定查找用户类文件的地位
-bootclasspath <path> 笼罩疏导类文件的地位
对象创立的流程剖析
class Person{// 类 Person
int age=90;
String name;
Person(String n,int a){// 结构器
name=n;// 给属性赋值
age=a;//..
}
}
Person p=new Person("TIMERRING",20);
流程剖析!
1. 加载 Person 类信息(Person.class) 到办法区,只会加载一次
2. 在堆中调配空间(地址)
3. 实现对象初始化[3.1 默认初始化 age=0 name=null 3.2 显式初始化 age=90,name=null,3.3 结构器的初始化 age =20, name=TIMERRING]
4. 在对象在堆中的地址, 返回给 p(p 是对象名, 也能够了解成是对象的援用)
this 关键字
能够失常运行,然而是否能够将构造函数的形参改为属性值呢?能够用 this。
public class This01 {
// 编写一个 main 办法
public static void main(String[] args) {Dog dog1 = new Dog("大壮", 3);
System.out.println("dog1 的 hashcode=" + dog1.hashCode());
//dog1 调用了 info()办法
dog1.info();
System.out.println("============");
Dog dog2 = new Dog("大黄", 2);
System.out.println("dog2 的 hashcode=" + dog2.hashCode());
dog2.info();}
}
class Dog{ // 类
String name;
int age;
// public Dog(String dName, int dAge){// 结构器
// name = dName;
// age = dAge;
// }
// 如果咱们结构器的形参,可能间接写成属性名,就更好了
// 然而呈现了一个问题,依据变量的作用域准则
// 结构器的 name 是局部变量,而不是属性
// 结构器的 age 是局部变量,而不是属性
//==> 引出 this 关键字来解决
public Dog(String name, int age){// 结构器
//this.name 就是以后对象的属性 name
this.name = name;
//this.age 就是以后对象的属性 age
this.age = age;
System.out.println("this.hashCode=" + this.hashCode());
}
public void info(){// 成员办法, 输入属性 x 信息
System.out.println("this.hashCode=" + this.hashCode());
System.out.println(name + "\t" + age + "\t");
}
}
java 虚构机会给每个对象调配 this, 代表以后对象。
this.name 就是 以后对象 的属性name。
深刻了解 this
暗藏的 this 指向本人的堆地址。
this 的注意事项和应用细节
- this 关键字能够用来 拜访本类的属性、办法、结构器
- this 用于辨别以后类的属性和局部变量
- 拜访成员办法的语法:this. 办法名(参数列表);
-
拜访结构器语法:this(参数列表); 留神只能在结构器中应用 (即只能在结构器中拜访另外一个结构器, 必须放在第一条语句)
public class ThisDetail { // 编写一个 main 办法 public static void main(String[] args) {// T t1 = new T(); // t1.f2(); T t2 = new T(); t2.f3();} } class T { String name = "jack"; int num = 100; /* 细节: 拜访结构器语法:this(参数列表); 留神只能在结构器中应用(即只能在结构器中拜访另外一个结构器) 留神:拜访结构器语法:this(参数列表); 必须搁置第一条语句!!! */ public T() {// 这里去拜访 T(String name, int age) 结构器 this("jack", 100); System.out.println("T() 结构器"); } public T(String name, int age) {System.out.println("T(String name, int age) 结构器"); } //this 关键字能够用来拜访本类的属性 public void f3() { String name = "smith"; // 传统形式(依照就近准则,有局部变量先拜访局部变量)System.out.println("name=" + name + "num=" + num);//smith 100 // 也能够应用 this 拜访属性(精确地就拜访属性)System.out.println("name=" + this.name + "num=" + this.num);//jack 100 } // 细节: 拜访成员办法的语法:this. 办法名(参数列表); public void f1() {System.out.println("f1() 办法.."); } public void f2() {System.out.println("f2() 办法.."); // 调用本类的 f1 // 第一种形式 f1(); // 第二种形式 this.f1();} }
- this 不能在类定义的内部应用,只能在类定义的办法中应用。
this 的案例
public class TestPerson {
// 编写一个 main 办法
public static void main(String[] args) {Person p1 = new Person("mary", 20);
Person p2 = new Person("mary", 20);
System.out.println("p1 和 p2 比拟的后果 =" + p1.compareTo(p2));
}
}
/*
定义 Person 类,外面有 name、age 属性,并提供 compareTo 比拟办法,用于判断是否和另一个人相等,提供测试类 TestPerson 用于测试,
名字和年龄齐全一样,就返回 true, 否则返回 false
*/
class Person {
String name;
int age;
// 结构器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//compareTo 比拟办法
public boolean compareTo(Person p) {return this.name.equals(p.name) && this.age == p.age;
}
}
文章和代码曾经归档至【Github 仓库:https://github.com/timerring/java-tutorial】或者公众号【AIShareLab】回复 java 也可获取。