1、类和对象:
类:抽象的、概念的一类事物,如人类。
对象:具体的、实际的具体事物。如小明这个人。
2、类和对象的创建:
public class CreateObject {
public static void main(String[] args) {
Cat cat1 = new Cat();
cat1.name = "小花";
cat1.age = 12;
cat1.color = "白色";
System.out.println(cat1.name + cat1.age + cat1.color);
}
}
class Cat{
String name;
int age;
String color;
}
//1、先声明再创建:
Cat cat;
cat = new Cat();
//2、直接创建:
Cat cat = new Cat();
3、对象内存布局:
1、栈:一般存放基本数据类型(局部变量)
2、堆:存放对象、数组等
3、方法区:存放常量池(常量、字符串),类加载信息
3.1、流程简单分析:
1、先加载类信息(方法区中:属性、方法信息),只会加载一次。
2、在堆中分配空间,进行默认初始化:如int=0;
3、将堆中地址赋值给栈中的对象名,该对象名就指向该对象。
4、进行指定初始化:比如:p.name=“jack”;
4、属性注意细节:
1、属性访问范围:public、protectted、默认、private。
2、属性的定义可以为基本数据类型或者引用类型。
3、属性如果不赋值,有默认值,和数组规则一样:int/short/byte/long:0;
float/double:0.0;char:\u0000;Boolean:false;String:null
5、对象分配机制:
Person p1 = new Person();
p1.age = 10;
p1.name = "李四";
Person p2 = p1;
System.out.println(p2.age);
6、空指针异常:NullPointException
当对象地址为空时,再去访问就会出现空指针异常
Person p = new Person();
p.age = 10;
p.name = "李四";
p=null;
System.out.println(p.age); // 报错 NullPointException
7、对象方法:
public class Person {
String name;
int age;
String sex;
/*
1、public:表示是一个公开方法。
2、void:表示该方法没有返回值。
3、dance():dance是方法名;():形参列表
*/
public void dance() {
System.out.println("我会唱歌");
}
}
方法的调用:
Person p = new Person();
p.dance();
8、方法的使用细节:
1、一个方法最多有一个返回值。如果要返回多个值,可以返回一个数组,将返回值放在数组里面。
2、返回类型可以为任意类型,包含基本类型和引用类型(数组、对象)。
3、如果方法要求有返回类型,则方法体最后执行的语句必须为return值。
4、void方法可以不用return语句,或者只写return。
5、使用驼峰命名法,如getSum。
9、参数列表:
1、一个方法可以没有或者有任意多个参数。
2、参数类型可以为任意类型,包含基本类型或者引用类型。
3、形参、实参类型及个数要一致。
10、方法调用细节:
1、同一个类的方法可以直接调用,不用创建对象来调用。
2、跨类方法要创建对象,使用对象名来调用。
11、方法传参机制:
1、对于基本数据类型,传递的是值(值拷贝),形参的任何改变不会影响实参。
调用方法的会会在栈中再开辟一个方法空间,他和main方法是两个不同的空间,因此方法空间里面发生的形参改变不会影响到实参。
2、对于引用数据类型:传递的是地址值,两个指向同一个空间,因此,形参发生改变也会影响到实参。
11、练习:
1、克隆对象:
public class CreateObject {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "lisa";
p1.age = 10;
MyTools myTools = new MyTools();
Person p2 =myTools.copyPerson(p1);
}
}
class Person {
String name;
int age;
}
class MyTools {
/*1、 Person:该方法返回类型为Person对象类型,
返回类型不可以随意写,必须是以及存在的类。
2、(Person p):形参为Person对象,对象名为ps
就和形参类型为 int,形参名为 num一样,其实形参里面起什么名都可以,
他只是一个形参,只是接收形参的。
3、新建一个person对象,对象名为p2
4、将形参对象person的姓名、年龄赋值给p2
*/
public Person copyPerson(Person ps) {
Person p2 = new Person();
p2.name = ps.name;
p2.age = ps.age;
return p2;
}
}
11、方法递归:
1、即方法自己调用自己。
方法递归输出结果是从后往前输出的。即没调用一个方法会开辟一个栈,由最后一个栈往前输出。 当输出到main栈时,如果后面没有代码就结束执行,main方法后面还有代码则继续执行。
2、递归阶层:
int res = factorial(5);
System.out.println(res); // 120
public int factorial(int n) {
if(n == 1) {
return 1;
} else {
return factorial(n -1) * n;
}
}
3、递归调用机制:
1、每次执行一个方法时,就创建一个新的受保护的独立空间(栈空间),每个栈空间里面的变量都是独立的,不会互相影响。
2、如果方法中使用的是引用类型的变量(如数组、对象),就会共享该引用数据类型的数据。
3、递归必须向退出递归的条件逼近,否则就是无线递归,出现:StackOverflowError。
4、当一个方法执行完毕,或者遇到了return,就会返回,遵守谁调用这个方法就把结果返回给谁(从上往下一级一级返回结果,一直到最前面)。
11、递归练习题:
1、猴子吃桃问题:猴子每天吃一半桃子+1个,到了第10天,发现只剩1个桃子了。
规则:前一天的桃子 = (后一天桃子 + 1) * 2 ; 后一天:day + 1
public int Peach(int day) {
if(day == 10) {
return 1;
} else if (day >= 1 && day <= 9) {
retuen (peach(day + 1) + 1) * 2;
}
}
2、走迷宫:
public class MiGong {
public static void main(String[] args) {
int map[][] = new int[8][7];
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[i].length; j++) {
map[0][j] = 1;
map[7][j] = 1;
map[i][0] = 1;
map[i][6] = 1;
map[3][1] = 1;
map[3][2] = 1;
}
}
Find find = new Find();
find.findWay(map,1,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.println();
}
}
}
class Find {
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; // 将可以走通的路数字设置为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; // 3:表示死路,四边都走不通
return false;
}
} else {
return false;
}
}
}
}
12、方法重载:方法名一致,形参列表不同即可。
基本介绍:
Java中允许在一个类中,多个同名方法的存在,到要求形参列表不一致。
注意事项:
1、方法名:必须一致。
2、形参列表:必须不同(形参类型或个数或顺序至少有一样不同,参数名无要求:int n 和 int a 是一样的)。
3、返回类型:无要求。
重载的意义:
1、减轻了起名的麻烦。
2、减轻了记名的麻烦。
public class Overload {
public static void main(String[] args) {
Methods methods = new Methods();
int cal1 = methods.cal(12);
int cal2 = methods.cal(12,22);
String cal3 = methods.cal("hello");
System.out.println(cal1 + " " + cal2 + " " + cal3);
}
}
class Methods{
public int cal(int n) {
return n * n;
}
public int cal(int n1, int n2) {
return n1 * n2;
}
public String cal(String str) {
return str;
}
}
13、可变参数:…
定义:将方法名相同、功能相同但是参数列表不同的方法封装成一个方法
要求:求2个数、3个数、4个数的和
如果用重载来解决的话就需要定义多个不同的方法,但是这些方法功能都是一样的,唯一不同的就是参数列表。
// int... 就是参数个数可以为(0~多个)
// sums:参数名
// 可以将可变参数当做数组,他也有大小
public int sum(int... sums) {
int res = 0;
for(int i = 0; i < sums.length; i++) {
res += sums[i];
}
return res;
}
使用细节:
1、可变参数的实参可以是0或者任意多个。
2、可变参数的实参可以为数组,可变参数的本质就是数组。
public class Overload {
public static void main(String[] args) {
Methods methods = new Methods();
int[] arr = {1, 2, 3};
int sum = methods.sum(arr);
System.out.println(sum);
}
}
class Methods{
public int sum(int... nums) {
int res = 0;
for (int i = 0; i < nums.length; i++) {
res+=nums[i];
}
return res;
}
}
3、可变参数可以和普通类型参数一起放在形参列表,但必须保证可变参数在最后。
public int sum(int n1, int n2 ,int... nums) {
int res = 0;
for (int i = 0; i < nums.length; i++) {
res+=nums[i];
}
return res;
}
14、作用域:
总结:作用域就是离自己最近的外层大括号范围就是变量的作用域
1、在Java中,主要的变量就是属性(成员变量)和局部变量。
2、我们说的局部变量一般是指在成员方法中定义的变量。
3、Java作用域分类:
全局变量:作用域为整个类,该类中的方法可以使用。
局部变量:作用域为定义它的代码块中。
4、
全局变量可以不赋值,直接使用,因为有默认值。
局部变量必须赋值后,才能使用,因为没有默认值。
class Methods{
int num;
String name = "Mary";
public void find() {
int age;
String address = "china";
System.out.println(num);
System.out.println(name);
// System.out.println(age); // 报错,局部变量未赋值不可直接使用
}
作用域注意事项:
a、属性(成员变量)和局部变量可以重名,访问时遵循就近原则。
class Methods{
String name = "Mary";
public void find() {
String name = "King";
System.out.println(name); // king
}
}
b、在同一个作用域中,两个局部变量不可重名。
c、属性生命周期较长,伴随着对象的创建而创建,随着对象的死亡而死亡。
局部变量,生命周期较短,伴随着代码块的执行而创建,伴随着代码块的结束而死亡。
6、区别:
全局变量(属性):可以在本类使用,也可以在其它类使用(通过对象调用)。
局部变量:只能在本类的方法中使用。
修饰符不同:全局可以加修饰符,局部不可以加。
15、构造器/构造方法
它是类的一种特殊方法,主要作用是完成对新对象的初始化。
以前我们给对象赋值的时候都是在对象创建完成之后赋值:对象名.属性名来赋值,如:p.name = lisa;
有了构造方法之后,就可以在创建对象的同时给对象赋值。
public class Construction {
public static void main(String[] args) {
Persons persons1 = new Persons("lisa", 12);
Persons persons2 = new Persons("lisa");
}
}
class Persons {
String name;
int age;
public Persons(String pName, int pAge) {
name = pName;
age = pAge;
}
public Persons(String pName) { // 构造器重载
name = pName;
}
}
1、特点:
1、方法名必须和类名一致。
2、没有返回值。
3、在创建对象时,系统会自动调用该类的构造器完成对象的初始化。
2、使用细节:
1、一个类可以定义多个不同构造器,及构造器的重载(构造器本质是一个方法,方法就可以重载)。
2、构造器是完成对象的初始化,并不是创建对象。
3、在创建对象时,系统自动调用该类的构造器,不能自己去调用:如:方法名.构造器名。
4、如果没有写构造器,那么程序会默认生成一个无参构造器。因此我们创建对象时都是这样创建的:
Dog dog = new Dog(); // 小括号里面是空的
claa Dog {
/*
dog() {} // 默认生成
*/
}
5、如果我们自己定义了一个构造器,那么原来的无参构造器就被覆盖了,不能再使用了(创建对象时必须初始化)。如果还想使用无参构造器,那么必须显性的定义一下。
// 利用无参构造器,设置所有人的性别都是男:
dog() {
sex = "男";
]
对象创建流程分析:
1、首先在方法区中加载类信息,只会加载一次。
2、在堆中分配空间(地址)。
3、完成对象初始化(构造器)【1、首先默认初始化 int=>0;String=> null。 2、显性初始化age=90;name=null。3、构造器初始化:age=20;name=小倩】
4、将对象在堆中的地址返回给p(p是对象名/对象的引用)。
16、this:
Java虚拟机会给每个对象都分配this,因此this指向的就是该对象自己。
哪个对象调用,this就指向哪个对象。
使用this升级构造器:
class Persons {
String name;
int age;
public Persons(String name, int age) {
/*下面是错误写法:忽略了变量作用域原则。
name = name; 这个两个name都是构造器里面的name,左边那个不是上面的全局name
age = age;
*/
this.name = name; // this.name:就是这个对象的name,那么就是上面的全局name了
this.age = age;
}
public Persons(String pName) { // 构造器重载
name = pName;
}
}
this使用细节:
1、this关键字可以用来访问本类的属性(使用this的话访问的是全局变量)、方法、构造器。
2、this可以用来在构造器中访问另一个构造器,必须写在第一条语句。this(参数列表);
public Cat() {
this("lisa", 100);
}
public Cat(String name, int age) {
}
3、this不能在类定义的外部使用,只能在类定义的方法中使用。