类与对象
public class demo{
public static void main(String[] args) {
//创建对象
cat cat1 = new cat();
cat1.name = "mike";
cat1.age = 12;
cat1.color = "rad";
System.out.println(cat1.name);
}
}
class cat{
//属性/成员变量 field字段
String name;
int age;
String color;
}
内存
注意事项
- 属性的定义语法同变量 :访问修饰符 属性类型 变量名
成员方法
public class demo{
public static void main(String[] args) {
//创建对象
cat cat1 = new cat();
cat1.name = "mike";
cat1.age = 12;
cat1.color = "rad";
cat1.speak();
System.out.println(cat1.name);
}
}
class cat{
//属性/成员变量 field字段
String name;
int age;
String color;
public void speak(){
system.out.println("gogo");
}
}
方法调用机制
访问修饰符
作用是控制方法的使用范围
如果不写 默认访问(public,protected,默认,private)
返回数据类型
-
一个方法最多有一个返回类型
-
返回类型可以为任意类型
形参列表
方法定义时的参数称为形式参数,简称形参;方法调用时的传入参数称为实际参数,简称为实参。实参和形参的类型要一致或兼容、个数、顺序必须一致。
同类方法
直接调用
public class Method01{
public static void main(String args[]){
A a = new A();
a.sayOk();
}
}
class A {
public void print(int n){
System.out.println(n+" ");
}
public void sayOk(){
print(10);
B b = new B();
b.going();
System.out.println("go");
}
}
class B{
public void going (){
System.out.println("B is running");
}
}
成员方法传参机制
引用类型传递的是地址
和C++指针差不多
public class MethodP{
public static void main(String[] args) {
// int a = 10 ;
// int b = 20;
A test = new A();
person p = new person();
p.age = 100;
p.name = "Mike";
test.test(p);
System.out.println(p.age); //99
}
}
class person{
String name;
int age;
}
class A{
public void test(person p){
p.age = 99;
p = null;
}
}
public class MethodP{
public static void main(String[] args) {
// int a = 10 ;
// int b = 20;
A test = new A();
person p = new person();
p.age = 100;
p.name = "Mike";
test.test(p);
System.out.println(p.age);//100
}
}
class person{
String name;
int age;
}
class A{
public void test(person p){
// p.age = 99;
p = null;
}
}
递归
public class dfs{
public static void main(String[] args) {
int map[][] = new int[5][6];
for(int i = 0;i<6;i++){
map[0][i] = 1;
map[4][i] = 1;
}
for(int i = 0;i<5;i++){
map[i][0] = 1;
map[i][5] = 1;
}
map[2][1] = 1;
map[2][2] = 1;
for(int i = 0;i<map.length;i++){
for(int j=0;j<map[i].length;j++){
System.out.print(map[i][j]);
}
System.out.print("\n");
}
sumAcc test = new sumAcc();
test.DFS(map,1,1,0);
System.out.println(test.sum);
}
}
class sumAcc{
int sum = 0;
int indexMap[][] = new int [5][6];
int zou[][] = {{1,0},{0,1},{-1,0},{0,-1}};
public void DFS(int map[][],int i, int j,int t){
// indexMap[i][j]=1;
if(i==3&&j==4){
sum++;
return;
}
// System.out.println(i+" "+j);
for(int k = 0;k<4;k++){
if((i+zou[k][0]<5)&&(i+zou[k][0]>0)&&(j+zou[k][1]>0)&&(j+zou[k][1]<6)&&(indexMap[i+zou[k][0]][j+zou[k][1]]==0)&&(map[i+zou[k][0]][j+zou[k][1]]==0)){
indexMap[i+zou[k][0]][j+zou[k][1]]=1;
// System.out.println(t);
DFS(map,i+zou[k][0],j+zou[k][1],t++);
indexMap[i+zou[k][0]][j+zou[k][1]]=0;
}
}
}
}
// 汉诺塔
public class hanTower{
public static void main(String[] args) {
Tower tower = new Tower();
tower.move(2,'a','b','c');
}
}
class Tower{
public void move(int num,char a,char b,char c){
if(num == 1){
System.out.println(a + "->" + c);
}else{
move(num-1, a ,c , b);
System.out.println(a + "->" + c);
move(num-1,b,a,c);
}
}
}
重载
java中允许多个方法存在,通过传递的参数不同来调用合适的方法。和C++一样。
方法名:必须相同
形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
返回类型:无要求
public void method(int a,int b){
...
}
public int method(int a,int c){
....
}
//错误使用,返回类型并无要求 两者传的参数一样,导致错误
可变参数
java允许将同种方法,多个参数的调用
使用可变参数时,可以当作数组来使用。
public class varParameter{
public static void main(String[] args) {
var v = new var();
System.out.println(v.sum(1,2,3));
}
}
class var{
public int sum(int... nums){
int res = 0;
for(int i = 0;i < nums.length; i++){
res += nums[i];
}
return res;
}
}
-
可变参数的实参个数是0或者多个
-
可变参数的实参可以是数组
-
可变参数的本质就是数组
-
可变参数可以和普通参数一起在形参列表,但是可变参数需要在最后
作用域
-
在java编程中,主要的变量就是属性(成员变量)和局部变量。
-
局部变量一般是指在成员方法中定义的变量。
-
java中的作用域的分类
-
全局变量:属性,作用域为整个类体
-
局部变量:除属性之外的其他变量,作用域为定义它的代码块中,
-
-
全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用, 因为没有默认值。
属性和变量可以重名,就近原则。
作用域范围不同
全局变量可以被本类调用,也可以被其他类调用。
局部变量只可以被本类的方法访问。
属性可以加修饰符
局部变量不能加修饰符
构造器/构造方法
[修饰符]方法名(形参列表){
方法体
}
-
构造器的修饰符可以默认,也可以是public…
-
构造器没有返回值
-
方法名和类名字必须一样
-
参数列表和成员一样的规则
-
构造器的调用系统完成
构造方法又叫构造器,是类的一种特殊的方法。它的主要作用是完成对新对象的初始化
-
方法名和类相同
-
没有返回值
-
在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。
细节
-
一个类可以定义多个不同的构造器,即构造器重载
-
构造器名和类名相同
-
构造器没有返回值
-
构造器是完成对象的初始化,并不是创建对象。
-
在创建对象时,系统自动调用该类的构造方法。
-
如果程序员没有定义定义构造器,系统会自动给类生成一个默认的无参构造器(默认构造器)
-
class Dog{ Dog(){ }//系统创建的默认构造器 }
-
public class Construct{
public static void main(String[] args) {
Person p1 = new Person("mike", 12);
System.out.println(p1.name);
// Person p = new Person();
// p.name = "mike"; 错的
}
}
class Person{
String name;
int age;
public Person(String Pname,int Page){
name = Pname;
age = Page;
}
public Person(String Pname){
name = Pname;
}
}
javap
反汇编
流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1eYegGBe-1647795839394)(/Users/zhaofei/Desktop/截图/截屏2021-10-03%20上午4.00.36.png)]
流程分析
1.加载Person类信息,只会加载一次
2.在堆中分配空间
3.完成对象初始化【1.默认初始化 age=0,name = null,显示初始化 age = 90 name =null,构造器初始化 name= 小倩 age=20】
4.在对象在堆中的地址返回给p
this 关键字
this 就是指向当前对象。this就是哪个对象调用,就指向哪个对象
public class this{
public static void main(String[] args) {
Person p1 = new Person("mike", 12);
System.out.println(p1.name);
}
}
class Person{
String name;
int age;
public Person(String name,int age){
name = name;//name指的是局部变量构造器的
age = age;//age同理
this.name = name ;
this.age = age; //this就指向对象的属性
}
public Person(String Pname){
name = Pname;
}
}
使用细节
-
this关键字可以用来访问本类的属性、方法、构造器
-
this用于区分当前类的属性和局部变量
-
访问成员方法的语法:this.方法名(参数列表)
-
this构造器语法:this(参数列表);只能在构造器中使用
-
this不能在类定义的外部使用,只能在类定义的方法中使用
class T{
public void f1(){
System.out.println("f1");
}
public void f2(){
System.out.println("f2");
f1();
this.f2();
}
}
class T1{
/*
细节:访问构造器语法:this(参数列表)
只能在构造器中使用,
必须放置在第一条语句
*/
String name;
int age;
public T1(){
this("mike",12);
System.out.println(this.name);
}
public T1(String name,int age){
this.name = name;
this.age = age;
System.out.println("name,age");
}
}
修饰符
-
public:公开
-
受保护级别:protected修饰,对子类和包使用
-
默认:没有修饰符
-
私有:private
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5QM5mq6C-1647795839395)(/Users/zhaofei/Desktop/截图/截屏2021-10-03%20上午7.14.52.png)]
只有默认和public才可以修饰类
封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作,才能对数据进行操作。
封装的好处
-
隐藏实现细节
-
可以对数据进行验证,保证安全合理
步骤:
1)将属性私有化
2)提供一个公共的set方法,用于对属性判断并赋值
3)提供一个公共的get方法,用于获取属性的值
继承
继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
class 子类 extends 父类{
}
子类就会自动拥有父类定义的属性和方法
父又叫超类,基类
子类又叫派生类
子类继承了所有的属性和方法,非私有的属性和方法可以在子类中直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
-
子类必须调用父类的构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不回通过
super只能在构造器的第一行和this()使用构造器一样。所以这两个不能共存
- java所有类都是Object的子类
- 子类最多只能继承一个父类(直接继承),java中是单继承机制。
- 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
查找关系:
-
如果子类有这个属性,并且可以访问,则返回信息
-
接着父类,上级父类。
public class Details01 {
public static void main(String[] args) {
son son = new son();
System.out.println(son.name);
}
}
class Granda {
String name = "granda";
String hobby = "123";
int age =33;
}
class father extends Granda {
String name = "father";
private int age = 99;
}
class son extends father {
String name = "son";
}
super关键字
super代表父类的引用,用于访问父类的属性和方法和构造器等。
访问父类的属性,但不能访问父类的private
属性
访问父类的方法,但不能访问父类的private
方法
-
调用父类的构造器(分工明确)
-
当子类中和父类有重名时,为了访问父类,必须通过super,没有重名时 时一样的效果
-
super的访问不限于直接父类,如果爷爷类和本类中有同名成员,也可以使用,按就近原则
public class A {
public int n1 = 888;
}
class B extends A{
public int n1 = 100;
public void test(){
System.out.println(this.n1);
System.out.println(super.n1);
}
}
public class test {
public static void main(String[] args) {
B b = new B();
b.test();
}
}
//100
//888 super直接查找了父类
重写
子类的方法名称和父类的某个方法的名称、返回类型、参数一样,那我们就说父类的这个方法覆盖了父类的方法。
子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类
子类方法不能缩小父类方法的访问权限
多态
方法或对象具有多种形态。多态是建立在封装和继承之上的。
-
一个对象的编译类型和运行类型可以不一致,父类的引用指向子类的对象
-
Animal animal = new Dog();[animal 编译类型是Animal,运行类型是Dog] animal = new Cat();【运行类型变成了Cat,编译类型仍然是Animal】
-
-
编译类型在定义对象时,就确定了
-
运行类型是可以改变的
-
编译类型看定义=号左边,运行类型看=右边
多态的前提是:两个类是继承关系
向上转型
父类的引用指向了子类的对象
Animal animal = new Dog();
-
可以调用父类的所有成员(遵守访问权限),但不可以调用子类特有的成员。
-
方法最终运行效果还得看子类的具体实现
Animal animal = new Dog();[animal 编译类型是Animal,运行类型是Dog] animal = new Cat();【运行类型变成了Cat,编译类型仍然是Animal】 cat.eat();//animal中有eat(),Dog中也有,那么调用的是Dog中的,也就是子类
public class test1 { public static void main(String[] args) { A b = new B(); System.out.println(b.age);//输出的是10 因为属性没有重写,根据编译类型算 } } class A{ public int age = 10; } class B extends A{ public int age = 20;
}
向下转型
语法:子类 引用名 = (子类类型)父类引用;
只能强转父类的引用,不能强转父类的对象
要求父类的引用必须指向的是当前目标类型的对象
当向下转型后,可以调用子类的所有成员
public class test1 {
public static void main(String[] args) {
A b = new B(); //b是父类引用
B c = (B) b;
System.out.println(c.age); //20这时已经是B了
}
}
class A{
public int age = 10;
}
class B extends A{
public int age = 20;
}
属性没有重写一说,属性根据编译类型来算
instanceOf
比较操作
动态绑定机制
java的动态绑定机制
-
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
-
当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。
class A{
public int i = 10;
public int sum(){
return getI()+10;
}
public int sum1(){
return i+10;
}
public int getI(){
return i;
}
}
class B extends A{
public int i =20;
public int sum(){
return i+20;
}
public int getI(){
return i;
}
public int sum1(){
return i+10;
}
}
A a = new B();
System.out.print(a.sum());//40 -> B.sum() i=20 20+20
System.out.print(a.sum1();//30->B.sum() i=20 i+10;
-------------------------------------------------------------------------
class B extends A{
public int i =20;
// public int sum(){
// return i+20;
// }
public int getI(){
return i;
}
//public int sum1(){
// return i+10;
// }
}
A a = new B();
System.out.print(a.sum());//B.sum()没有->A.sum() -> B.getI()->i=20;->20+10
System.out.print(a.sum1();// 20
多态数组
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
使用特有方法
Object类详解
enquals
==和equals的对比
==:比较运算符
-
既可以判断基本类型,也可以判断引用类型
-
如果判断基本类型,判断的值是否是相等的
-
如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
equals方法
-
Object中的方法,只能判断引用类型
-
默认判断的是地址是否相等,子类往往重写该方法,用于判断内容是否相等。比如Integer,String
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
----------------------------------------------------------
//string
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Hashcode
-
提高具有哈希结构的容器的效率
-
两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
-
两个引用,如果指向的是不同对象,则哈希值是不一样的
-
哈希值主要根据地址号来的,不能完全将哈希值等价于地址
-
后面在集合,一般会重写
toString
默认返回:全类名+@+哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息
重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString的形式
当直接输出一个对象时,toString会被默认调用
finalize
-
当对象被回收时,系统调用该对象的finalize方法,子类可以重写该方法
-
什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,在销毁该对象前,会先调用finalize方法
-
垃圾回收机制的调用,是由系统来决定的,也可以通过System.gc()主动触发垃圾回收机制
package com.object_;
package com.object_;
public class finalize_ {
public static void main(String[] args) {
Car car = new Car("bwm");
car = null;//指向此时断开,car就变成垃圾,垃圾回收器就会回收对象,在销毁对象前,会调用该对象的finalize方法
//程序员就可以在finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接)
//如果不重写,接回调用object的finalize
System.gc();
System.out.println("gogo");
}
}
class Car{
private String name;
public Car(String name) {
this.name = name;
}
public void finalize() throws Throwable{
System.out.println(this.name+"is running");
}
}
//gogo
//bwmis running
断点调试
在断点调试过程中,是运行状态,是以对象的运行类型来执行的
调试看源码
force into