Java——面向对象编程基础部分
单独变量不利于数据的管理
数组对于数据类型体现不出来
类与对象
对象拥有行为和属性
某个类,是自己定义的数据类型,对象就是一个具体的实例
从类到对象:创建一个对象,实例化一个对象,把类实例化
Java最大的特点就是面向对象
class Cat{
String name;
int age;
}
Cat cat1=new Cat();//把创建的猫赋给cat1
cat1.name="a";
cat1.age=9;
类是抽象的,概念的,代表一类事物,比如人类,猫类,即是一种数据类型
对象是具体的,实际的代表一个具体的事物,即是实例
类是对象的模板,对象是类的一个个体,对应一个实例
对象和数组都是引用类型
对象在内存中的存在形式
对象存放在栈,指向一个地址,该地址是存放在堆中的属性;(在栈中的对象实际上是对象的引用,对象名)
属性存放在堆,里面的基本数据类型直接存放在堆区,字符串则存放在方法区的常量池里,堆里只存放字符串的地址(属性数据等创建的对象空间才是真正的对象)
在执行new语句创建对象时,会把类信息加载到方法区,而后进行分配空间
属性/成员变量/field(字段)
属性是类的一个组成部分,可以时基本数据类型,也可以是引用类型(对象,数组)
细节说明
1.属性的定义语法与变量相同,但是增加了访问修饰符(控制属性的访问范围,共4种 public protected 默认 private)
格式:访问修饰符 属性类型 属性名
2.属性可以定义任意类型
3.属性如果不赋值,有默认值,规则和数组一致
创建对象和访问属性
创建对象
1.先声明再创建
Cat cat;//此时空间尚未分配
cat =new Cat();
2.直接创建
Cat cat=new Cat();
访问属性
对象名.name
内存分配机制
Java内存的结构分析
1.栈:一般存放基本数据类型(局部变量)
2.堆:存放对象(Cat 擦头,数组等)
3.方法区:常量池(常量,比如字符串),类加载信息(属性信息和方法信息)
4.示意图【Cat(name,age,price)】
流程简单分析
成员方法(方法)
类的行为
访问范围 返回值 方法名(形参列表){方法体}
方法写好后如果不去调用不会生效输出结果
先创建对象,再调用方法:对象名.方法
方法调用总结
1.当程序执行到方法时,就会开辟一个独立的空间
2.当方法执行完毕,或者执行到return语句时,就会返回
3.返回到调用方法的位置
4.返回后,继续执行方法后面的代码
5.完全执行完毕后main方法栈执行完毕,整个程序退出
创建一个类,将方法写入该类,使用时方法时创建类的对象,用对象调用成员方法即可
成员方法的好处
1.提高代码的复用性
2.可以将实现的细节封装起来,然后供其他用户来调用即可
定义
访问修饰符 返回数据类型 方法名(形参列表){
//方法体语句
return 返回值;
}
1.形参列表:表示成员方法输入 cai(int n)
2.数据类型(返回类型):表示成员方阿飞输出,void表示没有返回值
3.方法主体:表示为了实现某一功能代码块
4.return语句不是必须的
注意细节
访问修饰符:控制方法使用的范围,如果不写默认访问
返回数据类型:
1.一个方法最多只能有一个返回值,如果是多个返回值可以选择返回 数组
2.返回类型可以是任意类型,包括基本类型或者引用类型
3.如果方法要求有返回数据类型,则方法体最后一句必须为return 值,而且要求返回值类型必须和return的值类型一致或兼容
4.如果方法是void,则方法中可以某一return语句,或者只写return
方法名:
遵循驼峰命名法,方法名要有一定含义,注意开发规范
形参列表:
1.一个方法可以有0哥参数,也可以有多个参数,中间用逗号隔开
2.参数类型可以为任意类型,包含基本类型或引用类型
3.调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
4.方法定义时的参数称为形式参数,方法调用是参数为实际参数,实参和形参的类型要一致或兼容,个数和顺序必须一致
方法体:
里面写完成功能的具体语句,可以为输入输出,变量,运算,分支,循环,方法调用,但是里面不能再定义方法
方法补嵌套定义
方法细节调用说明:
1.同一个类中的方法调用:直接调用即可
2.跨类中的方法A类调用B类方法:需要通过对象名调用:对象名.方法名(参数)
先创建B类对象,然后再调用
3.跨类调用和方法的访问修饰符相关
4.方法调用会产生新栈
成员方法传参机制
基本数据类型的传参机制
基本数据传递的是值(值拷贝吗,在方法的栈中再生成一份),形参的任何改变不影响实参
基本数据类型的传参机制:
引用类型传递的是地址(传递的也是值,但是值是地址),可以通过形参影响实参
特殊实例:
方法的递归调用
递归的实例:
递归的重要规则
1.执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2.方法的局部变量是独立的,不会相互影响
3.如果方法种使用的数引用类型变量(比如数组,对象),就会共享该引用类型
4.递归必须向退出递归的条件逼近,否则就是无限递归
5.当应该方法执行完毕,或者遇到return,就会返回,遵守谁调用就将结果返回谁,同时方法释放空间的控制器权和分配权
迷宫问题
import java.awt.image.ImageProducer;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int[][] map = new int[8][7];
for (int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
for (int i = 0; i < 8; i++) {
map[i][0] = 1;
map[i][6] = 1;
}
map[3][1] = 1;
map[3][2] = 1;
}
T t1=new T();
t1.findWay(map,1,1);
}
class T{
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;
}
}
}
}
汉诺塔
import java.util.*;
public class Main {
public static void main(String[] args) {
T tower= new T();
tower.move(5,'A','B','C');
}
}
class T{
public void move(int num,char a,char b,char c){
if(num==1){
System.out.println(a+"->"+c);
}else{
//可以看成两个,最下面的和上面的所有(num-1)
//先移动上面所有的盘到b,借助c
move(num-1,a,c,b);
//把最下面的盘移动到c
System.out.println(a+"->"+c);
//再把b的所有移动到c,借助a
move(num-1,b,a,c);
}
}
}
运行结果:
A->C
A->B
C->B
A->C
B->A
B->C
A->C
八皇后问题
方法重载
Java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
方法重载减轻了起名,记名的麻烦
使用细节
1.方法名必须相同
2.形参列表必须不同(参数的类型,个数或顺序,至少有一样不同,参数名无要求)
3.返回类型没有要求
方法重载也存在自动转换类型,但是无需自动转换类型的优先级更高
可变参数
Java中允许将同一个类中多个同名同功能单数参数个数不同的方法,封装成一个方法
访问修饰符 返回类型 方法名(数据类型...形参名){}
使用可变参数时,可以当作数组来使用(遍历)
class Test{
public int sum(int... nums){
//可以接收多个int 遍历
int res=0;
for(int i=0;i<nums.length;i++)
{
res+=nums[i];
return res;
}
}
}
使用细节
1.可变参数的实参可以为0或者任意多个
2.可变参数的实参可以为数组
3.可变参数的本质就是数组
4.可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
5.一个形参列表中国只能出现一个可变参数
实例:
class Test{
public String show(String name,double... nums)
{
int res=0;
for(int i=0;i<nums.length;i++)
{
res+=nums[i];
}
return name+"成绩:"+res;
}
}
作用域
1.Java中主要的变量就是属性(成员变量)和局部变量
2.局部变量一般是指在成员方法中定义的变量
3.Java作用域的分类:
全局变量:也就是属性,作用域为整个类体
局部变量:也是除了属性之外的其他变量,作用域为定义它的代码块
4.全局变量(属性)可以不赋值,直接使用,因为有默认值,局本部变量必须赋值后才能使用,因为没有默认值
5.属性在定义时可以直接赋值
使用细节
1.属性和局部变量可以重名,访问时遵循就近原则
2.在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名(重复定义变量)
3.属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡,局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡,即在一次方法调用过程中
4.作用域不同:全局变量可以被本类使用,或被其他类使用(通过对象调用),局部变量只能在本类中对应的方法使用
5.修饰符不同:全局变量可以加修饰符,局部变量不可以加修饰符
构造方法/构造器
修饰符 方法名(形参列表){方法体}
1.构造器的修饰符可以默认,也可以时public private protected
2.构造器没有返回值,也不能写void‘
3.方法名必须和类名一样
4.参数列表和成员方法一致
5.构造器的调用,由系统完成
构造方法时类的一种特殊方法,主要作用是完成对新对象的初始化
1.方法名和类名相同
2.没有返回值
3.创建对象时,系统会自动的调用该类的构造器完成对象的初始化
使用细节
1.一个类可以定义多个不同的构造器,即构造器重载
2.构造器时完成对对象的初始化,不是创建对象
3.如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(默认构造方法)
4.一旦定义了自己的构造器,默认的构造器就被覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下默认无参构造器
javap能对给定的class文件提供字节代码进行反编译
javap -c -v 类名
每创建一个对象,都要调用构造函数,都要开辟一个新空间
流程分析:
创建对象
先默认初始化
再进行显式初始化
构造器处理进行属性初始化
返回对象的地址
(对象实际在堆里,命名是对象的引用)
1.加载Person类信息,只会加载一次
2.在堆中分配空间(地址)
3.完成对象的初始化(默认初始化,显示初始化,构造器初始化)
4.对象在堆中的地址返回给p,p是对象名也可以理解成对象的引用
this关键字
Java虚拟机会给每个对象分配this,代表当前对象
哪个对象调用,this就代表哪个对象
验证:hashCode方法会针对不同的对象返回不同的整数(将对象的内部地址转换成一个整数来实现的)
使用细节
1.this关键字可以用来访问本类的属性,方法,构造器
2.this用于区分当前类的属性和局部局部变量
3.访问成员方法语法:this.方法名(形参列表)
class T{
//访问成员方法的语法:this.方法名(参数列表)
public void f1(){
System.out.println("f1");
}
public void f2(){
System.out.println("f2");
//调用本类的f1
//第一种方式
f1();
//第二种方式
this.f1();
}
}
4.访问构造器语法:this(参数列表),只能在构造器中使用(只能在构造器中访问另外一个构造器,必须放在第一条语句)
public T(){
//访问T(String,int)
this("jack",100);
System.out.println("T() 构造器");
}
public T(String name,int age){
System.out.println("T(String,int) 构造器");
}
}
//注意:访问构造器语法this必须放在构造器的第一条语句
5.this不能在类定义的外部使用,只能在类定义的方法中使用
注意:访问区别
实例:
import java.util.*;
public class Main{
public static void main(String[] args) {
Person p1=new Person("mary",12);
Person p2=new Person("mary",12);
System.out.println("比较结果:"+p1.compareTo(p2));
}
}
class Test{
}
class Person{
public String name;
public int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public boolean compareTo(Person p){
// if(this.name.equals(p.name)&&this.age==p.age)
// return true;
// else
// return false;
return this.name.equals(p.name)&&this.age==p.age;
}
}
运行结果:
比较结果:true
注意
任何类都可以有main
匿名对象只能使用一次,使用过后即销毁
实例作业
但是只能用一个,因为在构造器中访问另一个构造器的this语句必须放在第一句
import java.util.*;
public class Main {
public static void main(String[] args) {
Circle c=new Circle();
PassObject po=new PassObject();
po.printAreas(c,5);
}
}
class Circle{
double radius;
public Circle(){}
public Circle(double radius)
{
this.radius=radius;
}
public double findArea()
{
return Math.PI*radius*radius;
}
public void setRadius(double radius)
{
this.radius=radius;
}
}
class PassObject{
public void printAreas(Circle c,int times)
{
System.out.println("radius\tarea");
for(int i=1;i<=times;i++)
{
c.setRadius(i);
System.out.println((double)i+"\t"+c.findArea());
}
}
}
猜拳游戏
import java.util.*;
public class Main {
public static void main(String[] args) throws IllegalAccessException {
Tom t=new Tom();
int isWinCount=0;
int[][] arr1=new int[3][3];
int j=0;
String[] arr2=new String[3];
Scanner sc=new Scanner(System.in);
for(int i=0;i<3;i++)
{
System.out.println("请输入你要出的拳:");
int num=sc.nextInt();
t.setTomNum(num);
int tomGuess=t.getTomNum();
arr1[i][j+1]=tomGuess;
int comGuess=t.computerNum();
arr1[i][j+2]=comGuess;
String isWin=t.vsComputer();
arr2[i]=isWin;
arr1[i][j]=t.count;
System.out.println(t.count+"\t"+tomGuess+"\t\t"+comGuess+"\t\t");
isWinCount=t.winCount(isWin);
}
for(int a=0;a<arr1.length;a++)
{
for(int b=0;b<arr1[a].length;b++)
{
System.out.print(arr1[a][b]+"\t\t\t");
}
System.out.print(arr2[a]);
System.out.println();
}
System.out.println("you win"+isWinCount);
}
}
class Tom{
int tomNum;
int comNum;
int winCount;
int count=1;
public void showInfo(){
}
public int computerNum(){
Random r=new Random();
comNum=r.nextInt(3);//返回0-2的随机数
return comNum;
}
public void setTomNum(int tomNum) throws IllegalAccessException {
if(tomNum>2||tomNum<0){
throw new IllegalAccessException("数字输入错误");
}
this.tomNum=tomNum;
}
public int getTomNum() {
return tomNum;
}
public String vsComputer(){
if(tomNum==0&&comNum==1){
return "you win";
}else if(tomNum==1&&comNum==2)
{
return "you win";
}else if(tomNum==2&&comNum==0)
{
return "you win";
} else if (tomNum==comNum) {
return "平局";
}else
return "you false";
}
public int winCount(String s){
count++;
if(s.equals("you win")){
winCount++;
}
return winCount;
}
}