类和对象
类是一种数据类型
对象是具体的,实际的,代表一个具体的事物
类是对象的模板,对象是类的一个个体,对应一个实例
import java.util.Scanner;
public class Array05{
public static void main(String[] args){
//单独变量定义,麻烦
String cat01 = "jeck";
String cat02 = "mary";
int cat01age = 4;
int cat02age = 100;
String cat01color = "white";
String cat02color = "black";
Scanner scanner = new Scanner(System.in);
//数组 ===>数据类型无法体现
String[] cat01 = {"jeck","3","white"};
//使用oop面向对象来解决
//实例化一只猫[创建一个猫对象]
Cat cat01 = new Cat();
cat01.name ="jeck";
cat01.color = "white";
cat01.age = 3;
cat02.name ="mary";
cat02.color = "black";
cat02.age = 100;
//使用
System.out.println("第一只猫的名字"+ cat01.name + "颜色和年龄:"+ cat01.age +" " + cat01.color);
}
}
//定义一个猫类Cat -> 自定义的数据类型
class Cat{
//属性
String name;
int age;
String color;
}
对象在内存中的存在形式
注意事项和细节
属性的定义语法同变量 示例:访问修饰符 属性类型 属性名;四种访问修饰符:public proctected 默认 private
属性的定义类型可以为任意类型,包括基本类型和引用类型
属性如果不赋值,默认值规则和数组一致
如何创建对象
1、Cat cat;
cat = new Cat();先声明再创建
2、直接创建
Cat cat = new Cat();
类和对象内存分配机制
1、栈:一般存放基本数据类型(局部变量)
2、堆:存放对象
3、方法区:常量池(常量,比如字符串),类加载信息(属性信息和方法信息)
java创建对象基本流程
Person p = new Person();
p.name = “jack”;
p.age = 10;
1、先加载person类信息
2、在堆中分配空间,进行默认初始化,把对象在堆内地址返回给p
3、把地址赋给p,p就指向对象
4、进行指定初始化
成员方法(方法)
1、当程序执行到方法时,就会开辟一个独立的空间(栈空间)
2、当方法执行完毕,或者执行到return语句时,就会返回
3、返回到调用方法的地方,继续执行后面的代码
4、当main方法执行后,整个程序退出,栈空间被销毁
具体方法调用实例
优点:提高代码复用性
可以将实现的细节封装起来,供其他用户调用
public class Method01{
public static void main(String[] args){
//方法使用
//方法写好后如果不调用,不会生效
//先创建对象,再调用方法
Person p1 = new Person();
p1.speak();//调用方法
p1.cal01();
p1.cal02(5);//调用cal02方法,把5传给n;
//把getSum方法返回的值赋给变量returnRes;
int returnRes = p1.getSum(5,10);//传入两个参数
System.out.println(returnRes);
}
}
class Person {
String name;
int age;
//方法
//添加speak成员方法,输出我是一个好人
//public 表示方法是公开的,void 表示方法没有返回值
//speak是方法名()形参列表
//{}方法体,写我们需要执行的代码
public void speak(){
System.out.println("我是一个好人");
}
public void cal01(){
int res = 0;
for(int i=1;i<=1000;i++){
res+=i;
}
System.out.println(res);
}
//传入一个形参
public void cal02(int n){
int res = 0;
for(int i=1;i<=n;i++){
res+=i;
}
System.out.println(res);
}
//添加getSum方法,可以计算两个数的和
//int 表示它一定会返回一个int值,可以接收两个形参(用户可以传入两个数)
//return res;返回值res
public int getSum(int num1,int num2) {
int res = num2 + num1;
return res;
}
}
成员方法的使用:
public 返回数据类型 方法名(参数列表){方法体
语句
return 返回值;
其中参数列表表示输入的参数 返回数据类型表示方法的输出数据类型(void表示没有返回值),return表示返回的值,不一定要有return
返回数据类型的注意事项和使用细节
1、一个return只能返回一个返回值,多个结果可以返回数组。返回类型可以是任意类型;
2、如果方法要求有返回值,那么最后依据必须是return语句,返回值类型必须和方法定义的类型一致或者兼容,符合自动类型转换规则。
3、void 可以没有return,或者只有return(后面不能带具体的值)。
形参列表注意事项和使用细节
1、方法可以有0个参数或者多个参数,参数间用逗号隔开。
2、调用方法实际传进去的值叫实参,而方法中的两个数叫形参;两个参数类型,个数、顺序必须一致;
方法中不能再定义方法,方法不能嵌套定义。
方法调用细节
1、同一个类中的方法可以互相调用,直接调用,不需要创建对象;
class A{
public void print(int n){
System.out.println("print方法被调用" + n);}
public void sayok(int n){
print(10);
System.out.println("print方法被调用" + n);}
public void M1(){
B b = new B();
b.HI();
System.out.println("m1方法被调用");}
}
class B{
public void HI(){
System.out.println("HI方法被调用");}
}
2、跨类别方法调用:需要通过创建对象名调用
3、调用还与访问修饰符有关。
成员方法的传参机制(parameter)
基本数据类型传递的是值,形参的改变不影响实参
引用数据类型的传参机制
引用类型传递的是一个地址,可以通过形参影响实参
克隆一个对象,两个不是同一个对象但是有相同的属性
public class Method04{
public static void main(String[] args){
Person p1 = new Person();
p1.age = 15;
p1.name = "jack";
Mytools tools = new Mytools();
//引用方法需要新建一个类再从这个类中找方法,最后引用
Person p2 = tools.copyPerson(p1);
System.out.println(p1.name + p1.age);
System.out.println(p2.name + p2.age);
}
}
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;
}
}
方法的递归调用
在方法中调用方法,让程序更加的简洁;
递归的具体使用内存1、打印问题
从最底层开始输出,依次往外输出。
当执行一个方法时就会创建一个新的受保护的空间
方法的局部变量是独立的,不会互相影响
如果方法中使用的是引用类型变量,就会共享该引用类型的数据
递归必须想退出递归的条件逼近,不然就会死循环
当一个方法执行完毕时,或者遇到return就返回,遵守谁调用谁返回
通过递归实现小老鼠走迷宫和汉诺塔问题
具体的实现思路已经在代码中给出,但是递归最重要的就是找到退出条件,会分析栈中内存图。
public class MiGong{
public static void main(String[] args){
//小老鼠走迷宫问题
//先创建迷宫,用二维数组表示 int[][] map = new int [8][7];
//2、0表示可以走的路,1表示障碍物
int[][] map = new int [8][7];
//地图绘制,将最上下两行全部设置为1;
for(int i=0;i<map.length;i++){
for(int j = 0;j<map[i].length;j++){
if( i==0 || i==7){
map[i][j] = 1;
}
else if(j==0||j==6){
map[i][j] = 1;
}
}
}
map[3][1] = 1;
map[3][2] = 1;
//使用findWay给老鼠找路
T t1 = new T();
t1.findWay(map,1,1);
System.out.println("找路的情况如下:");
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.println();
}
}
}
class T{
//使用递归回溯思想来解决
//专门找出迷宫的路径,如果找到返回ture,没有则返回false
//map就是迷宫,i,j就是老鼠的位置
//初始化位置为[1][1]
//规定推出条件map数组的各个值的含义
//0表示可以走的路,1表示障碍物2表示可以走3表示走过但是走不通
//当map[6][5] = 2时,找到通路,退出,否则继续找路
//先确定老鼠找路的策略,下面走不通走右边-》上-》左
//策略也可以改变,路径很可能不一样
public boolean findWay(int[][] map,int i,int j){
if(map[6][5] == 2){//总退出递归调用的条件
return true;
}
else{
if(map[i][j] == 0){
//假定可以走通
map[i][j] =2;
//使用找路策略开始测试该位置是否真的可以走通
if(findWay(map,i+1,j)){
return true;
}
else if(findWay(map,i,j+1)){
return true;
}
else if(findWay(map,i-1,j)){//上
return true;
}
else if(findWay(map,i,j-1)){
return true;
}
else{
map[i][j] = 3;
return false;
}
}
else{
return false;
}
}
}
}
//汉诺塔移动的步骤输出
public class HanoiTower{
public static void main(String[] args){
Tower t1 = new Tower();
t1.move(2,'A','B','C');
}
}
class Tower{
//a,b,c表示3根柱子num表示盘子数
public void move(int num,char a ,char b, char c){
if(num ==1){
System.out.println(a+"->" + c);
}else {
//所有的盘子都看出只有两个盘,分最下面一个和上面的
//首先把a塔上面的那个盘移动到b塔,可以借助c塔
move(num-1,a,c,b);//把a移动到b后剩下的就是a->c,所以输出a->c
//最下面的盘移动到c
System.out.println(a+ "->" + c);//a当前剩下的盘子移动到c
//再把b柱子上的所有盘移动到c,借助a塔
move(num-1,b,a,c);
}
}
}
方法重载机制
java允许同一个类中,多个同名方法的存在,但要求形参列表不一致。
减轻了起名和记名的麻烦。
使用注意事项
1、方法名称必须一样
2、形参的类型或个数或者顺序必须有一个不一样,参数名称没有要求
3、返回类型没有要求
可变参数
1、Java允许将同一个类中多个同名同功能但是参数个数不同的方法,封装成一个方法,可以通过可变参数实现
2、基本语法: 访问修饰符 返回类型 方法名(数据类型…形参名){}
可变参数的实参可以是数组
可变参数可以和普通参数方法一个参数列表,但是必须保证可变参数在最后。
一个形参列表只允许有一个可变参数
class M{
//使用可变参数时,可以把输入的同类型数据当作数组使用
//int...表示输入任意个int类型的数据。
public int sum(int... nums){
System.out.println("接收的参数个数为" + nums.length);
int res = 0;
for(int i=0;i<nums.length;i++){
res +=nums[i];
}
}
}
public class VarParameter01{
public static void main(String[] args){
S m = new S();
String grade = m.Sum("jack",56,78);
System.out.println(grade);
}
}
class S{
public String Sum(String name, int...nums){
int res = 0;
//String[] grade = new String[2];
for(int i=0;i<nums.length;i++){
res += nums[i];
}
return name + "成绩总分为" + res;
}
}
作用域
在Java中,主要的变量就是属性(成员变量)和局部变量(在成员方法中定义的变量)
作用域分类:全局变量,作用域为整个类体;而局部变量就是除了属性之外的其他变量,作用域为定义它的代码块中,全局变量可以不赋值,直接使用,而局部变量必须赋值后才能使用,因为没有默认值。
注意事项
*属性和局部变量可以重名,访问时遵循就近原则。
*同一个作用域中,两个局部变量不可以同名
*属性生命周期较长,随着对象的创建和死亡,而局部变量则是随着代码块的创建和死亡,只是一次方法的调用过程。
*全局变量(属性):可以在本类中使用,也可以通过创建对象被其他类调用,形参也可以传入对象
*局部变量:只能被自己的类调用
*修饰符:全局变量(属性)可以加修饰符;局部变量不可以加修饰符
构造器/构造方法(constructor)
基本语法:[修饰符] 方法名(形参列表){
方法体;}
主要作用:完成对新对象的初始化,不创建对象,在创建对象时,系统自动调用构造器。
1、构造器的修饰符可以默认,也可以其他;
2、构造器没有返回值
3、方法名和类名字必须一样
4、参数列表和成员方法一样规则
5、构造器的调用,由系统完成
6、构造器也叫构造方法,也可以进行方法的重载
7、如果没有写构造器,系统会自动生成一个默认无参构造器(类名(){})
javap反编译,把class转成代码。
8、一旦自定义了构造器,默认无参构造器被覆盖,无法使用,除非重新定义一个无参构造器。
public class Constructor01{
public static void main(String[] args){
Person p1 = new Person("smith",98);//new后,系统会自动调用构造器
Person p2 = new Person();
}
}
class Person{
String name;
int age=90;
//完成对象属性的初始化
public Person(String pName,int pAge){
name = pName;
age = pAge;
}
public Person(){
age = 18;
}
}
流程分析:1、加载Person类信息,只加载一次
2、在堆中分配地址
3、完成对象初始化(首先默认初始化age = 0,name =null,显式初始化,age
=90,name=null,构造器初始化 age = 20,name=null。)
4、把对象在堆中的地址返回给p1或者p2。
当属性名和构造器形参名称一样时,会遵循就近原则,其中的name和age只是局部变量而不是属性,无法给属性赋值。这个问题可以通过this关键字来解决。
this关键字
this.name this.age当前对象的属性,不再时局部变量的age和name(this代表当前对象)
hashCode:返回对象的哈希码值,就是把他的地址转换成整数输出
this 关键字可以用来访问本对象的属性、方法和构造器;
this用于区分当前类的属性和局部变量
访问成员方法的语法:this.方法名(参数列表);
访问构造器语法this(参数列表):注意只能在构造器中使用;(只能在构造器中区调用另一个构造器)
this不能再类定义的外部使用,只能在类定义的方法中使用。
public class Constructor01{
public static void main(String[] args){
Person p1 = new Person("smith",98);//new后,系统会自动调用构造器
Person p2 = new Person();
p1.f1();
}
}
class Person{
String name;
int age;
//完成对象属性的初始化
//在一个构造器中访问另一个构造器
public Person(){
this("jack",26);//访问下面的构造器,如果有this的语法,必须放在第一条语句。
}
public Person(String pName,int pAge){
name = pName;
age = pAge;
}
//想要将构造器中的名字简洁,使用this关键字。
public Person(String name ,int age){
this.age = age;
this.name =name;//age和name都是当前对象的属性
}
public void f1(){
//调用本类的方法
f1();
this.f1();
}
}
具体练习题
通过构造器来求圆的面积
public class Homework07{
public static void main(String[] args){
//通过定义两个类求出不同半径的圆面积
Circle c = new Circle();
PassObject p= new PassObject();
p.printAreas(c,5);
}
}
class Circle{
double radius;
public Circle(){
}
public Circle(double radius){
this.radius = radius;
}
public double findArea(){
return Math.PI*this.radius*this.radius;
}
//添加一个方法setRadius,修改对象半径值
public void setRadius(double radius){
this.radius = radius;
}
}
class PassObject{
public void printAreas(Circle c, int times){
System.out.println("Radius" + "\t" + "Area");
for(int i=1;i<=times;i++){
c.setRadius(i);
System.out.println(i + "\t" + c.findArea());
}
}
}