Java程序可以认为是一系列对象的集合 而这些对象通过调用彼此的方法来协同工作
类 calss
类是程序的基本单位 是对象的模板 它描述一类对象的状态和行为 是数据类型 是抽象的 概念的 代表一类事物
对象 object
对象是类的实例 有状态(属性)和行为(方法) 是具体的 实际的 代表一个具体事物 例如 一条狗是一个对象 它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等
创建
- 先声明再创建
Cat cat;//声明对象
Cat cat = new Cat();//创建 - 直接创建 Cat cat = new Cat();
内存分配机制
结构分析
- 栈:一般存放基本数据类型(局部变量)
- 堆:存放对象(Cat cat , 数组等)
- 方法区:常量池(常量,比如字符串) 类加载信息
流程分析
Cat cat=new Cat();
cat.name="小白";
cat.age=12;
- 在方法区加载Cat类信息(属性和方法信息 只加载一次)
- 在堆中分配空间 进行默认初始化(看规则)+显式初始化+构造器初始化
- 把对象在堆中的地址返回给cat cat就指向对象
- 进行指定初始化 比如
cat.name="小白";
//示例
Person p1 = new Person();
p1.name = “jack”;
p1.age = 10;
Person p2 = p1;
属性/成员变量/字段(field)
定义
属性是类的一个组成部分(状态)
一般是基本数据类型 也可是引用类型(对象,数组)
属性的定义语法同变量访问修饰符 属性类型 属性名;
属性如果不赋值 有默认值 规则和数组一致
int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000, boolean false,String null
访问
对象名.属性名
cat.name
方法/成员方法(函数)
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
方法也是类的组成部分(行为)
方法操作对象内部状态的改变 对象的相互调用也是通过方法来完成
在创建一个对象的时候 至少要调用一个构造器 构造器的名称必须与类同名 一个类可以有多个构造器
定义
访问修饰符 返回值类型 方法名(形参列表) {//方法体
语句;
return 返回值;
}
//例如
public void speak() {
System.out.println("我是一个好人");
}
调用机制
- 先在栈中开辟一个main方法的空间
- 执行到其他方法时 会在栈中开辟一个独立的空间
- 当方法执行完毕,或者执行到return语句时,就会返回,
- 返回到调用方法的地方
- 返回后,继续执行方法后面的代码
- 当main方法(栈)执行完毕,整个程序退出
好处
提高代码的复用性 将细节封装起来 供其他用户调用
注意事项
- 方法不能嵌套定义
- 形参实参类型要一致
- 一个方法最多有一个返回值
- 返回类型可以为任意类型 包含基本类型或引用类型(数组,对象)
- 如果方法要求有返回数据类型 则方法体中最后的执行语句必须为
return 值;
而且要求返回值类型必须和return的值类型一致或兼容 - 如果方法是void 则方法体中可以没有return语句 或者只写
return ;
- 访问修饰符(作用是控制 方法使用的范围) [有四种: public, protected, 默认, private] 如果不写默认访问
- 遵循驼峰命名法 最好见名知义 表达出该功能的意思即可 比如 得到两个数的和 getSum
调用
- 同类中的方法调用:直接调用
- 跨类中的方法调用(与修饰符相关):
对象名.方法名
public class xuexi {
//编写一个main方法
public static void main(String[] args) {
A a=new A();
a.print(10);//不在同一类 需要对象.方法名
}
}
class A{
public void print(int n){
System.out.println("输入的数字是"+n);
print2();//在同一类 直接调用即可
}
public void print2(){
System.out.println("print2调用完成");
}
}
传参机制
基本数据类型
传递的是值(值拷贝) 形参的改变不影响实参
public class Test {
public static void main(String[] args) {
int a=10;
int b=20;
AA c=new AA();
c.swap(a,b);
System.out.println("main方法交换后的值a="+a+" b="+b);
}
}
class AA{
public void swap(int a,int b){
System.out.println("交换前的值a="+a+" b="+b);
int temp=a;
a=b;
b=temp;
System.out.println("交换后的值a="+a+" b="+b);
}
}
/*
交换前的值a=10 b=20
交换后的值a=20 b=10
main方法交换后的值a=10 b=20
*/
引用数据类型
传递的是地址(地址也是值 但传递的值是地址) 可以通过形参影响实参
public class Test {
public static void main(String[] args) {
BB b = new BB();
int[] arr = {1, 2, 3};
b.shuzu(arr);
System.out.print("main中的arr数组 ");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
Person p=new Person();
p.name="jack";
p.age=10;
b.duixiang(p);
System.out.println("main中的p.age="+p.age);
}
}
class Person{
String name;
int age;
}
class BB{
public void shuzu(int[] arr){//传递数组
arr[0]=100;
System.out.print("shuzu中的arr数组 ");
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
System.out.println();
}
public void duixiang(Person p){//传递对象
p=new Person();
p.age=99;
}
}
/*
shuzu中的arr数组 100 2 3
main中的arr数组 100 2 3
main中的p.age=10
*/
递归调用
- 执行一个方法时 就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的 不会相互影响 比如n变量
- 如果方法中使用的是引用类型变量(比如数组 对象) 就会共享该引用类型的数据.
- 递归必须向退出递归的条件逼近 否则就是无限递归 出现StackOverflowError 死龟了:)
- 当一个方法执行完毕 或者遇到return 就会返回 遵守谁调用 就将结果返回给谁 同时当方法执行完毕或者返回时 该方法也就执行完毕
递归实例
猴子吃桃问题:
有一堆桃子 猴子第一天吃了其中的一半 并再多吃了一个 以后每天猴子都吃其中的一半 然后再多吃一个 当到第10天时 想再吃时(即还没吃)发现只有1个桃子了
问题:最初共多少个桃子?
public class Test {
public static void main(String[] args) {
T t=new T();
int day=1;
int num=t.peach(day);
if(num!=-1){
System.out.println("第"+day+"天有"+num+"个桃子");
}
}
}
class T {
public int peach(int day) {
if (day == 10) {//第十天只有一个桃子
return 1;
} else if (day >= 1 && day <= 9) {//前一天的桃子=(后一天的桃子+1)*2
return ((peach(day + 1) + 1) * 2);
} else {
System.out.println("day在1-10");
return -1;
}
}
}
/*
第1天有1534个桃子
*/
迷宫问题 汉诺塔问题 八皇后问题 p225
重载
定义
同一个类中 有多个同名方法 但形参列表不一致
egcaculate(int a,int b)caculate(double a,double b)
注意事项
- 方法名必须相同
- 形参列表必须不同(个数或顺序至少有一个不同 参数名无要求)
- 返回类型无要求
可变参数
定义
同一类中多个同名同功能但参数个数不同的方法 封装成一个方法
注意事项
- 实参可以为0个或任意多个 也可以为数组
- 本质就是数组
- 可变参数和普通类型的参数一起放在形参列表 但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数
访问修饰符号 返回类型 方法名(数据类型...形参名)
public class Test {
public static void main(String[] args) {
Sum m=new Sum();
System.out.println(m.sum(1,2));
System.out.println(m.sum(1,2,3));
System.out.println(m.sums(1,2));
System.out.println(m.sums(1,2,3));
}
}
class Sum {
//方法重载
public int sum(int a,int b){
return a+b;
}
public int sum(int a,int b,int c){
return a+b+c;
}
//可变参数
public int sums(int...nums){
System.out.println("接收的形参个数="+nums.length);
int res=0;
for(int i=0;i< nums.length;i++){
res+=nums[i];
}
return res;
}
}
/*
3
6
接收的形参个数=2
3
接收的形参个数=3
6
*/
有三个方法 分别实现返回姓名和两门课程总分 返回姓名和三门课程总分 返回姓名和五门课程总分 封装成一个可变参数的方法
public class Test {
public static void main(String[] args) {
Example s=new Example();
System.out.println(s.showScore("jack",90.1,80.0));
System.out.println(s.showScore("terry",90.1,80.0,70.4));
}
}
class Example {
public String showScore(String name,double...scores){
double totalscore=0;
for(int i=0;i<scores.length;i++){
totalscore+=scores[i];
}
return name+"有"+scores.length+"门课的总分为"+totalscore;
}
}
/*
jack有2门课的总分为170.1
terry有3门课的总分为240.5
*/
作用域
- 主要的变量有属性(成员变量)和局部变量(在成员方法中定义的变量)
- 作用域分为两种
- 全局变量(属性):作用域为整个类 有默认值 可以不赋值直接使用
- 局部变量(除了属性之外的其他变量):作用域为定义它的代码块中 没有默认值 必须赋值后使用
注意事项
-
- 属性和局部变量可以重名 访问时遵循就近原则
- 两个局部变量在同一个作用域中不能重名 如同一个成员方法中
-
- 属性生命周期较长 伴随对象创建而创建 销毁而销毁
- 局部变量生命周期较短 伴随它的代码块的执行而创建 结束而销毁
-
- 属性可以在本类使用 或在其他类使用(通过对象调用)
- 局部变量只能在对应的方法中使用
-
- 属性可以加修饰符
- 局部变量不可以加修饰符
构造方法/构造器
定义
类的一种特殊方法 作用是完成对新对象的初始化
语法
[修饰符] 方法名(形参列表){
方法体;
}
- 修饰符可以默认 可以是public protected private
- 没有返回值 也不能写void
- 方法名和类的名字必须相同
- 参数列表和成员方法规则相同
- 在创建对象时 系统会自动调用该类的构造器完成对象的初始化
注意事项
- 一个类可以定义多个不同的构造器 即构造器重载
- 构造器是完成对象的初始化 并不是创建对象
- 如果程序员没有定义构造器 系统会自动给类生成一个默认无参构造器(也叫默认构造器)
- 一旦定义了自己的构造器 默认的构造器就覆盖了 就不能再使用默认的无参构造器 除非显式的定义一下无参构造器
Dog(){}
- 构造器传入的数据是局部变量 不是属性
public class Test {
public static void main(String[] args) {
Person p1=new Person("king",40);//第一个构造器
Person p2=new Person("tom");//第二个构造器
Person P3=new Person();//第三个构造器
Dog dog=new Dog();//使用的是默认无参构造器
}
}
class Dog{
String name;
//Dog(){}默认构造器 如果自己定义了构造器 默认的无参构造器就被覆盖 不能再使用
public Dog(String dname){
name=dname;
}
Dog(){}//显式地定义一下无参构造器 就可以使用无参构造器了
}
class Person {
String name;
int age;
public Person(String pname,int page){//第一个构造器 指定人名和年龄
name=pname;
age=page;
}
public Person(String pname){//第二个构造器 只指定人名
name=pname;
}
public Person(){//第三个构造器 无参构造器 默认年龄18
age=18;
}
}
this关键字
public class Test {
public static void main(String[] args) {
Dog d1=new Dog("tom",3);
System.out.println("狗的名字是"+d1.name+"狗的年龄是"+d1.age);
//狗的名字是null狗的年龄是0
//因为构造器中的name和age是局部变量 而不是属性
}
}
class Dog{
String name;
int age;
// public Dog(String dname,int dage){
// name=dname;
// age=dage;
//如果能把dname和dage改成name和age就直观了
// }
public Dog(String name,int age) {
//构造器中的name和age是局部变量 而不是属性
//变量name和age是分配给自己 没有改变属性
name = name;
age = age;
}
}
this
关键字表示当前对象的xxx
使用
- 用来访问本类的属性 方法 构造器
- 哪个对象调用 this就代表哪个对象
- 访问成员方法:
this.属性名
- 访问成员方法:
this.方法名(参数列表)
- 访问同类其他构造器:
this(参数列表)
只能放在构造器第一句 - this不能在类定义的外部使用 只能在类定义的方法中使用
定义Person类属性 并提供compareTo比较方法 用于判断一个人是否和另一个人完全相等 提供测试类Test用于测试 若名字和年龄完全一样就返回ture 否则返回false
public class Test {
public static void main(String[] args) {
Person p1=new Person("mary",20);
Person p2=new Person("mary",20);
System.out.println("p1和p2比较的结果="+p1.equalTo(p2));
//p1和p2比较的结果=true
}
}
class Person{
String name;
int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public boolean equalTo(Person p){
return this.name.equals(p.name)&&this.age==p.age;
}
}
2.编写类AA 定义方法find 实现查找某字符串是否在字符串数组中 并返回索引 如果找不到 返回-1
public class Test {
public static void main(String[] args) {
String[] strs={"marry","jack","tom"};
AA a=new AA();
int n=a.find("tom",strs);
System.out.println("所查找字符串的索引是"+n);
}
}
class AA{
public int find(String str,String[] strs){
for(int i=0;i< strs.length;i++){
if(str.equals(strs[i])){
return i;
}
}
return -1;
}
}