Java内存的结构分析
- 栈:一般存放基本数据类型(局部变量)
- 堆:存放对象
- 方法区:常量池(常量,比如字符串),类加载信息
递归例题
斐波那契数列
public class test1{
public static void main(String args[]){
for(int i = 1; i <= 10; i ++){
System.out.println(Fib(i)+" ");
}
}
static int Fib(int n){
if (n <= 2)
return 1;
else
return Fib(n -1) + Fib(n - 2);
}
}
猴子吃桃
有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个。以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,发现只有一个桃子了,问:最初有多少个桃子?
思路:逆推
- day = 10 时 有1个桃子
- day = 9 时 有(day10 + 1) * 2 = 4个桃子
- day = 8 时 有(day9 + 1) * 2 = 10个桃子
- ……
- 规律:前一天的桃子 = (后一天的桃子 + 1) * 2
- 递归
public int peach(int day){
if(day == 10){
return 1;
}else if(day >= 1 && day <= 9){
return (peach(day + 1) + 1) * 2;
}else{
System.out.println("日期无效");
return -1;
}
}
迷宫问题
思路:
- 先创建迷宫,用二维数组表示,int [ ][ ] map = new int [8][7]
- 规定map数组的元素值:0表示可以走,1表示障碍物
- 将最上面1行和最下面一行全部设置为1
- 将最右面1列和最左面一列全部设置为1
- findWay方法找出迷宫的路径
- 如果找到,返回true;否则,返回false
- i,j是老鼠的位置,初始化的位置为(1,1)
- 递归找路,规定map数组中各个值的含义:0表示可以走,1表示障碍物,2表示可以走且已走过,3表示走过但走不通,是死路
- 当map[6][5] = 2就说明找到通路,退出结束;否则就继续找
- 确定老鼠找路策略 下->右->上->左
public class MiGong(String args[]){
//编写一个mian方法
public class 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 j = 0; j < 8; j ++){
map[j][0] = 1;
map[j][6] = 1;
}
map[3][1] = 1;map[3][2] =1;
//使用findWay给老鼠找路
Solution s = new Solution();
s.findWay(map, 1, 1);
//输出当前的地图
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
System.out.println(map[i][j] + " ");
}
System.out.println();
}
}
}
class Solution{
//使用递归回溯的思想解决老鼠出迷宫
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{//map[i][j] == 1/2/3
return false;
}
}
}
}
汉诺塔
public class HanoiTower {
public static void main(String[] args) {
Tower tower = new Tower();
tower.move(64, '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{
//先移动上面的所有盘到b,借助c
move(num - 1, a, c, b);
//把最下面的盘移动到c
System.out.println(a + "->" + c);
//再把b的所有盘移动到c,借助a
move(num - 1, b, a, c);
}
}
}
八皇后
方法重载(OverLoad)
基本介绍
Java允许同一个类中,多个同名方法的存在,但要求形参列表不一致
比如System.out.println(); out是PrintStream类型
重载的好处
减轻了起名和记名的麻烦
注意事项和使用细节
- 方法名:必须相同
- 形参列表:必须不同(形参类型或顺序,至少有一样不同,参数名无要求)
- 返回类型:无要求
可变参数
基本概念
Java允许将同一个类中多个同名同功能但参数个数不同的方法封装成一个方法。这可以通过可变参数实现。
基本语法
访问修饰符 返回类型 方法名(数据类型… 形参名){
}
注意事项和使用细节
- 可变参数的实参可以为0个或任意多个
- 可变参数的实参可以为数组
- 可变参数的本质就是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数
作用域
- 在Java编程中,主要的变量就是属性(成员变量)和局部变量。
- 局部变量一般指在成员方法中定义的变量。
- Java中作用域的分类:
(1)全局变量:也就是属性,作用域为整个类体局部变量
(2)局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中 - 全局变量可以不赋值,直接使用,因为有默认值;局部变量必须赋值后才能使用,因为没有默认值
注意事项和使用细节
- 属性和局部变量可以重名,访问时遵循就近原则
- 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名
- 属性声明周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁;局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁,即在一次方法调用过程中
- 作用域范围不同
(1)全局变量/属性:可以被本类使用,或其它类使用
(2)局部变量:只能在本类中对应的方法中使用 - 修饰符不同
(1)全局变量/属性:可以加修饰符
(2)局部变量:不可以加修饰符
构造方法/构造器
基本语法
[修饰符] 方法名(形参列表){
方法体;
}
说明
- 构造器的修饰符可以默认, 也可以是 public protected private
- 构造器没有返回值
- 方法名和类名字必须一样
- 参数列表 和 成员方法一样的规则
- 构造器的调用, 由系统完成
基本介绍
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。
注意事项和使用细节
- 一个类可以定义多个不同的构造器,即构造器重载
- 构造器名和类名要相同
- 构造器没有返回值
- 构造器是完成对象的初始化,并不是创建对象
- 在创建对象时,系统自动调用该类的构造方法
- 如果程序员没有定义构造器,系统会自动给类生成一个默认的无参构造器(也叫默认构造器)
- 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下
对象创建的流程分析
class Person{
int age = 90;
String name;
Person(String n, int a){//构造器
name = n;
age = a;
}
}
Person person = new Person("小倩", 20);
流程分析:
-
加载Person类信息(Person.class),只会加载一次
-
在堆中分配空间(地址)
-
完成对象初始化
(1)默认初始化 age = 0, name = null
(2)显式初始化 age =90, name = null
(3)构造器初始化 age = 20, name = 小倩 -
将对象在堆中的地址返回给p,p是对象名,也可以理解成是对象的引用
this关键字
什么是this:Java虚拟机会给每个对象分配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");
}
}
简单的说,哪个对象调用,this就代表哪个对象
this的注意事项和使用细节
- this关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表);注意只能再构造器中使用(即只能在构造器中访问另一个构造器,必须放在第一条语句)
- this不能再类定义的外部使用,只能在类定义的方法中使用