Java学习记录
前言
前面在APP测试中查看日志的时候有很多Java方面的异常信息,考虑到很多大型项目都是使用Java语言构建的,所以还是有必要学一点Java。由于之前学过python语言,这次学Java给我最大的感受就是:规矩多~。Java语言里的规矩要比python多得多,远没有python来得灵活。可能这也是其中的魅力之处吧。
先上手一个小游戏再说,培养一下兴趣。
import java.awt.*;
import java.util.concurrent.atomic.DoubleAdder;
import javax.swing.*;
public class BallGame2 extends JFrame{
//创建图片对象
Image ball = Toolkit.getDefaultToolkit().getImage("images/ball.png");
Image desk = Toolkit.getDefaultToolkit().getImage("images/desk.jpg");
double x=100;//小球的横坐标
double y=100;
double degree = 3.14/3;//弧度,60度
//画窗口的方法
public void paint(Graphics g){
System.out.println("窗口被画了一次!");
//先画桌面再画球
g.drawImage(desk, 0, 0,null);
g.drawImage(ball,(int)x,(int)y,null);
x = x + 10*Math.cos(degree);
y = y + 10*Math.sin(degree);
//碰到上下边界
if(y>500-40-30||y<40+40){ //500是窗口高度,40是桌子边框,另一个40是标题栏高度
degree = -degree;
}
//碰到左右边界
if(x<40||x>856-40-30){
degree = 3.14 - degree;
}
}
//窗口加载
void launchFrame(){
setSize(856,500);
setLocation(50,50);
setVisible(true);
//重画窗口,每秒50次,相当于视频50帧
while(true){
repaint();
try{
Thread.sleep(20); //20ms画一次
}catch(Exception e){
e.printStackTrace();
}
}
}
//main方法时程序执行的入口
public static void main(String[] args){
System.out.println("我是天青色,我在玩球");
//实例化对象
BallGame2 game = new BallGame2();
game.launchFrame();
}
}
实现结果图:
数据结构
注释
单行注释:使用“//”开头,“//”后面的单行内容均为注释。
多行注释:以“/*”开头以“*/”结尾,在“/*”和“*/”之间的内容为注释,我们也可以使用多行注释作为行内注释。但是在使用时要注意,多行注释不能嵌套使用。
文档注释:以“/**”开头以“*/”结尾,注释中包含一些说明性的文字及一些JavaDoc标签(后期写项目时,可以生成项目的API)
注释不会出现在字节码文件中,即Java编译器编译时会跳过注释语句。
标识符
标识符是用来给变量、类、方法以及包进行命名的,如Welcome、main、System、age、name、gender等。标识符需要遵守一定的规则:
-
标识符必须以字母、下划线_、美元符号$开头。
-
标识符其它部分可以是字母、下划线“_”、美元符“$”和数字的任意组合。
-
Java 标识符大小写敏感,且长度无限制。
-
标识符不可以是Java的关键字。
- 表示类名的标识符:每个单词的首字母大写,如Man, GoodMan
- 表示方法和变量的标识符:第一个单词小写,从第二个单词开始首字母大写,我们称之为“驼峰原则”,如eat(), eatFood()
保留字
保留字是Java语言保留供内部使用的,如class用于定义类。我们不能使用保留字作为变量名或方法名。
变量
变量本质上就是代表一个”可操作的存储空间”,空间位置是确定的,但是里面放置什么值不确定。我们可通过变量名来访问“对应的存储空间”,从而操纵这个“存储空间”存储的值。
Java是一种强类型语言,每个变量都必须声明其数据类型。变量的数据类型决定了变量占据存储空间的大小。 比如,int a=3; 表示a变量的空间大小为4个字节。
变量作为程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。变量在使用前必须对其声明, 只有在变量声明以后,才能为其分配相应长度的存储空间。
注意:
-
每个变量都有类型,类型可以是基本类型,也可以是引用类型。
-
变量名必须是合法的标识符。
-
变量声明是一条完整的语句,因此每一个声明都必须以分号结束。
三种变量类型的区别:
关于float:
浮点数可表示的值范围比同等位数的整数表示方式的值范围要大得多;
浮点数无法精确表示其值范围内的所有数值,而有符号和无符号整数则是精确表示其值范围内的每个数值;
控制语句
占坑
数组
数组简介
1 public class Array0101 { 2 3 public static void main(String[] args){ 4 System.out.println("今天是2020年01月01日,新年快乐!"); 5 // 如果不确定数组当中的具体内容,用动态初始化;否则,用静态。 6 int[] array1 = new int[] {3,4,5};//静态初始化:指定数组内容,根据内容可知长度。 7 int[] array2;//拆开 8 array2 = new int[]{1,2,3}; 9 10 String[] array3 = {"helo","world","java"};//静态可省略 new String[] 11 12 double[] array4 = new double[3];//动态初始化:指定数组长度 13 double[] array5;//动态拆开 14 array5 = new double[3]; 15 16 System.out.println(array1);//[I@15db9742 得到的是内存地址哈希值,[表示数组,I表示int型 17 System.out.println(array3[1]);//取第二个元素,world 18 int num = array1[2];//将数组中某个元素赋值给变量 19 System.out.println(num);//5 20 /*使用动态初始化数组的时候,其中的元素将会自动拥有一个默认值,规则如下: 21 * 整数类型:0; 22 * 浮点类型:0.0; 23 * 字符类型:‘\u0000’; 24 * 布尔类型:false; 25 * 引用类型:null; 26 */ 27 System.out.println(array4[2]);//0.0 28 array4[2] = 3.14;//改变元素 29 System.out.println(array4[2]);//3.14 30 } 31 32 }
打印字符数组
为什么打印字符数组得到的却是字符串,而打印整数数组得到的却是内存地址?
char类型的数组就相当于一个字符串。因为输出流System.out是PrintStream对象,PrintStream有多个重载的println方法,其中一个就是public void println(char[] x),直接打印字符数组的话,不像int[]等其他数组,它会直接调用这个方法来打印,因而可以打印出数组内容,而不是地址。
1 /* 2 char类型的数组就相当于一个字符串。 3 因为输出流System.out是PrintStream对象,PrintStream有多个重载的println方法, 4 其中一个就是public void println(char[] x),直接打印字符数组的话,不像int[]等其他数组, 5 它会直接调用这个方法来打印,因而可以打印出数组内容,而不是地址。 6 */ 7 8 public class Demo08StringConvert { 9 10 public static void main(String[] args) { 11 // 转换 12 char[] chars = {'a','b','c'}; 13 System.out.println(chars);//abc 14 int[] ints = {1,2,3}; 15 System.out.println(ints);//[I@15db9742 16 17 }
数组索引越界异常
数组的索引编号从0开始,一直到数组长度-1。如果访问数组的时候索引编号不存在,那么将会发生数组索引越界异常:
java.lang.ArrayIndexOutOfBoundsException
所有事物引用类型变量,都可以赋值为一个null值,代表其中什么都没有。
数组必须进行new初始化才能实用其中的元素。
获取数组长度:数组名称.length。
数组一旦创建,程序运行期间,长度不可改变。
数组遍历
for (int i = 0; i < array01.length; i++) { System.out.println(array01[i]); }
数组最大值
1 int[] array01 = {1,5,3,12,35,46,7,8,9,10}; 2 int max = array01[0]; 3 for (int i = 1; i < array01.length; i++) { 4 if(array01[i]>max){ 5 max = array01[i]; 6 } 7 8 } 9 System.out.println(max);//46
冒泡排序
1 //数组长度为10,则比9轮,第一轮比9次,第二轮比8次..,第9轮比一次 2 int[] array01 = {1,5,3,12,35,46,7,8,9,10}; 3 int max1; 4 for (int i = 0; i < array01.length-1; i++) { 5 for (int j = 0; j < array01.length-1-i; j++) { 6 if(array01[j]>array01[j+1]){ 7 max1= array01[j]; 8 array01[j] = array01[j+1]; 9 array01[j+1] = max1; 10 } 11 } 12 } 13 for (int i = 0; i < array01.length; i++) { 14 System.out.print(array01[i]+"、");//1、3、5、7、8、9、10、12、35、46、 15 }
数组翻转
1 //数组翻转 2 for(int i=0,j=array01.length-1;i<j;i++,j--){ 3 int temp = array01[i]; 4 array01[i] = array01[j]; 5 array01[j] = temp; 6 } 7 for (int i = 0; i < array01.length; i++) { 8 System.out.print(array01[i]+"、");//46、35、12、10、9、8、7、5、3、1、 9 } 10
数组作为参数
比如上面每次最后都要打印出数组,现在写一个打印数组的方法,每次调用方法即可。
1 //打印数组的方法 2 public static void printArray(int[] arr){ 3 for (int i = 0; i < arr.length; i++) { 4 System.out.println(arr[i]+"、"); 5 } 6 }
这里的形参int[] arr 是个数组类型,传递进去的是数组的内存地址值。如[I@15db9742。
数组作为返回值
1 public static int[] calculate(int a,int b,int c){ 2 int add = a+b+c; 3 int sum = (a+b+c)/3; 4 int[] arr = {add,sum}; 5 return arr; 6 }
int[] result = calculate(10, 20, 30);
System.out.println(result[0]);//60
Java的内存划分
分成五个部分:
(这里讲的与后面的有些许不同,源自不同机构,这里是传智黑马老师说的,面向对象那里是动力节点)
1、栈(Stack) :存放的都是方法中的局部变量。方法的运行一定要在栈当中运行。
局部变量:方法的参数,或者是方法内部的变量
作用域:一旦超出作用域,立刻从栈内存当中消失。
2、堆(Heap) :凡是new出来的东西,都在堆当中。
堆内存里面的东西都有一个地址值: 16进制
堆内存里面的数据,都有默认值。规则:
如果是整数 默认为0
如果是浮点数 默认为0.0
如果是字符 默认为’u0000‘
如果是布尔 默认为false
如果是引用类型 默认为null
3、方法区(Method Area) :存储.class相关信息,包含方法的信息。
4、本地方法栈(Native Method Stack) :与操作系统相关。
5、寄存器(pc Register) :与CPU相关。
数组内存分析
一个数组内存分析:
两个数组的内存分析:
两个引用指向相同的数组:
面向对象
面向对象和面向过程
面向过程(Procedure Oriented)和面向对象(Object Oriented,OO)都是对软件分析、设计和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。
面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”并将步骤对应成方法,一步一步,最终完成。 这个适合简单任务,不需要过多协作的情况下。比如,如何开车?我们很容易就列出实现步骤:
1. 发动车 2. 挂挡 3.踩油门 4. 走你
面向过程适合简单、不需要协作的事务。 但是当我们思考比较复杂的问题,比如“如何造车?”,就会发现列出1234这样的步骤,是不可能的。那是因为,造车太复杂,需要很多协作才能完成。此时面向对象思想就应运而生了。
面向对象(Object)思想更契合人的思维模式。我们首先思考的是“怎么设计这个事物?” 比如思考造车,我们就会先思考“车怎么设计?”,而不是“怎么按步骤造车的问题”。这就是思维方式的转变。
面向对象思想思考造车,发现车由如下对象组成:
1. 轮胎 2. 发动机 3. 车壳 4. 座椅 5. 挡风玻璃
为了便于协作,我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤;这样,发现大家可以同时进行车的制造,最终进行组装,大大提高了效率。但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的,还是离不开面向过程思想!
总结:
1、都是解决问题的思维方式,都是代码组织的方式。
2、解决简单问题可以使用面向过程
3、解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程。
4、遇到复杂问题,先从问题中找名词,然后确立这些名词哪些可以作为类,再根据问题需求确定的类的属性和方法,确定类之间的关系。
5、面向对象具有三大特征:封装性、继承性和多态性。
类
1.对象是具体的事物,类是对对象的抽象。
2.类可以看成一类对象的模板,对象可以看成该类的一个具体实例。
3.类是用于描述同一类型的对象的一个抽象概念,类中定义了这一类对象所应具有的共同的属性、方法。
4.每一个源文件必须有且只有一个public class,并且类名和文件名保持一致。
5.在定义成员变量时可以对其初始化,如果不对其初始化,Java使用默认的值对其初始化:
整型:0;浮点型:0.0 ;字符型:'\u0000' ;布尔型:false;所有引用类型:null
1 public class Phone { 2 //成员变量 3 String brand; 4 double price; 5 String color; 6 //成员方法 7 public void call(String toWho) { 8 System.out.println("给"+toWho+"打电话!"); 9 } 10 public void sendMessage() { 11 System.out.println("群发短信"); 12 } 13 14 }
方法
方法三要素:返回值,方法名称,参数列表;
方法的定义无先后顺序;
方法的定义不支持嵌套,不能在一个方法的内部再定义一个方法;只能在类里面定义;
方法定义后要调用才会执行;
方法是若干语句的功能集合;
return后面的返回数据必须和方法的返回值类型对应起来;
1 package day01; 2 /* 3 方法三要素:返回值,方法名称,参数列表; 4 方法的定义无先后顺序; 5 方法的定义不支持嵌套,不能在一个方法的内部再定义一个方法;只能在类里面定义; 6 方法定义后要调用才会执行; 7 方法是若干语句的功能集合; 8 */ 9 public class TestMethod { 10 public static void main(String[] args) { 11 printCount(10);//调用方法1,传参10 12 int result = add();//调用方法2并赋值 13 System.out.println(result); 14 print99();//调用方法3 15 } 16 17 //方法1打印指定次数的“hello world” 18 public static void printCount(int num) { 19 for (int i = 1; i <= num; i++) { 20 System.out.println("hello world"); 21 } 22 } 23 24 //方法2:求出1-100的整数和; 25 public static int add() { 26 int sum = 0; 27 for (int i = 1; i <= 100 ; i++) { 28 sum += i; 29 } 30 return sum; 31 } 32 33 34 // 方法3:打印99乘法表 35 public static void print99() { 36 for(int i=1;i<=9;i++ ){ 37 for(int j=1;j<=i;j++){ 38 System.out.print(j+"x"+i+"="+j*i+'\t'); 39 } 40 System.out.println(); 41 } 42 } 43 44 }
方法重载
方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。 调用时,会根据不同的参数自动匹配对应的方法。
重载的方法,实际是完全不同的方法,只是名称相同而已! 构成方法重载的条件:
形参类型、形参个数、形参顺序不同。
1 public class TestMethod { 2 3 public static void main(String[] args) { 4 // 方法 5 //通过对象调用普通方法 6 TestMethod tm = new TestMethod(); 7 tm.p(); 8 tm.p(); 9 double a = tm.add(1.0, 2.001, 3.0)+1; 10 System.out.println(a); 11 add(1,2); 12 } 13 14 15 void p(){ 16 System.out.println("hahahah"); 17 } 18 double add(double a,double b,double c){ 19 double sum =a+b+c;//6.0009999999999994 20 System.out.println(sum); 21 return sum; //返回值的类型在方法前标明 22 } 23 24 //重载方法 25 //java中方法可以同名,通过参数个数,类型,顺序区分 26 public static int add(int a,int b){//static则在主函数里直接调用 27 int sum = a+b; 28 System.out.println("public static int add:"+sum); 29 return sum; 30 } 31 32 }
注意:
1、方法前不加public则只能被同包里面的类调用;加了public则全局可调;
2、方法前加static则可在main里面直接调用,不加则要通过类的实例化对象去调用;
静态方法和实例化方法的区别
静态方法(static method)
与静态成员变量一样,属于类本身,在类装载的时候被装载到内存中,不自动进行销毁,会一直存在内存中,直到JVM关闭;
非静态方法(non-static method)
又称实例化方法,属于实例对象,实例化之后才会分配内存,必须通过类的实例来引用,当实例对象被JVM回收之后,也跟着消失
静态方法和实例方法的区别
1.生命周期
静态方法的生命周期从进程创建时就开始,一直到进程结束,所以说静态方法是全局的,贯穿整个进程
实例方法的生命周期,从实例化对象开始,一直到实例化对象被注销回收之后结束。
所以实例方法的生命周期是短于静态方法的生命周期,这也是实例方法中不能调用静态方法的原因。就好像我认识小乔,小乔却不认识我。小乔比我存在的早。
2.调用方式
在外部调用静态方法时,可以使用 “类名.方法名”的方式,也可以使用“对象.方法名”的方式,也就是说调用静态方法时无需创建对象。
实例方法只能使用“对象.方法名的方式”。
3.访问限制
静态方法在访问本类的成员时,只允许访问静态成员(即静态变量和静态方法),而不允许访问实例成员变量和实例方法;而实例方法则无此限制。
实例成员变量是属于某个对象的,在静态方法执行时,并不一定存在该对象;同理,如果允许静态方法访问实例成员方法,就间接的可以访问实例成员变量,所以也不能访问实例成员方法;基于同样的道理,静态方法中也不能使用关键字this。
4.执行顺序
当一个class文件被ClassLoader load进入JVM之后,方法指令保存在Stack中,此时Heap区并没有数据。然后程序技术器开始执行指令,如果是静态方法,直接依次执行指令代码,当然此时指令代码是不能访问Heapshuju数据区的;如果是实例方法(实例方法有一个隐含的传入参数,该参数是JVM给它的,这个参数就是实例对象在Stack中的内存地址,因此实例方法才可以找到在Heap中属于自己的数据),则在调用前必须实例化该对象,在Heap中分配数据,并将Stack中的内存指针通过JVM的隐含参数传给实例方法。若不实例化直接调用,由于隐含参数没有值,会报错。
1 package day02; 2 3 public class Static_01 { 4 int a = 1;//成员变量 5 static int aa = 2;//静态变量 6 7 public static void main(String[] args) { 8 9 add2();//直接调用静态方法 10 Static_01 s = new Static_01(); 11 s.add();//实例化后才能调用实例化方法 12 } 13 14 public void add(){//成员方法访问无限制 15 System.out.println(a); 16 System.out.println(aa); 17 add2(); 18 } 19 20 public static void add2(){//静态方法不能直接访问非静态 21 22 System.out.println(aa); 23 add3(); 24 System.out.println(a);//报错 25 add();//报错 26 27 } 28 public static void add3(){//静态方法 29 30 System.out.println("hehe"); 31 32 } 33 }
如果一个成员变量使用了static关键字,那么这个变量就不再属于对象自己,而是属于所在的类,多个对象共享同一份数据。
还是学生类:
1 public class Student { 2 private String name; 3 private int age; 4 static int cla; 5 6 public Student(){ 7 System.out.println("调用了无参构造方法"); 8 } 9 10 public Student(String name,int age){ 11 System.out.println("调用了全参构造方法"); 12 this.name = name; 13 this.age = age; 14 15 } 16 public void setName(String name){ 17 this.name = name; 18 } 19 public String getName(){ 20 return name; 21 } 22 public void setAge(int age){ 23 this.age = age; 24 } 25 public int getAge(){ 26 return age; 27 } 28 }
1 public class Demo01Static { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 Student stu1 = new Student("wy",19); 6 Student.cla = 101;//对静态变量赋值 7 System.out.println("学生的姓名是:"+stu1.getName()+ 8 ",年龄是:"+stu1.getAge()+",班级时:"+stu1.cla);//学生的姓名是:wy,年龄是:19,班级时:101 9 10 Student stu2 = new Student("zww",18); 11 System.out.println("学生的姓名是:"+stu2.getName()+ 12 ",年龄是:"+stu2.getAge()+",班级时:"+stu2.cla);//学生的姓名是:zww,年龄是:18,班级时:101 13 14 } 15 }
内存分析
静态代码块
当第一次用到本类时,静态代码块执行唯一的一次。 静态内容总是优先于非静态,所以静态代码块比构造方法先执行。 典型用途:用来一次性的对静态成员变量赋值。
1 package day0108; 2 /* 3 当第一次用到本类时,静态代码块执行唯一的一次。 4 静态内容总是优先于非静态,所以静态代码块比构造方法先执行。 5 典型用途:用来一次性的对静态成员变量赋值。 6 */ 7 8 public class Demo02StaticCode { 9 10 public static void main(String[] args) { 11 // TODO Auto-generated method stub 12 Person one = new Person(); 13 Person two = new Person(); } 14 } 15 16 class Person { 17 Person(){ 18 System.out.println("构造方法执行"); 19 } 20 21 static{//静态代码块 22 System.out.println("静态代码块执行"); 23 } 24 25 }
执行结果:
成员变量和局部变量的区别
局部变量和成员变量
1、定义的位置不一样
局部变量:在方法的内部
成员变量:在方法的外部,直接写在类当中
2、作用范围不一样
局部变量:只有方法当中才可以使用,出了方法就不能再用
成员变量:整个类全都可以通用
3、默认值不一样
局部变量:没有默认值,如果要想使用,必须手动进行赋值
成员变量:如果没有赋值,会有默认值,规则和数组一样
4、内存的位置不一样
局部变量:位于栈内存
成员变量:位于堆内存
5、生命周期不一样
局部变量:随着方法进栈而诞生,随着方法出栈而消失
成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失
1 public class Variable { 2 int a;//成员变量 3 4 public void methodA(){ 5 int b;//局部变量 6 System.out.println(a); 7 System.out.println(b); //报错 8 } 9 10 public void methodB(){ 11 int c;//局部变量 12 System.out.println(c); //报错 13 } 14 }
面向对象的内存分析
Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area。
栈:
1. 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等),执行完就关闭了;
2. 启动一个程序后会有很多线程,JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等);
3. 栈属于线程私有,不能实现线程间的共享;
4. 栈的存储特性是“先进后出,后进先出”;
5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间;
堆:
1. 堆用于存储创建好的对象和数组(数组也是对象);
2. JVM只有一个堆,被所有线程共享;
3. 堆是一个不连续的内存空间,分配灵活,速度慢;
方法区:
1. JVM只有一个方法区,被所有线程共享;
2. 方法区实际也是堆,只是用于存储类、常量相关的信息;
3. 用来存放程序中永远不变或唯一的内容。(类信息【Class对象】、静态变量、字符串常量等);
以下是一个学生类:
1 public class Student { 2 //成员变量 3 String name; 4 int age; 5 6 //成员方法 7 public void eat() { 8 System.out.println("吃饭饭"); 9 } 10 public void sleep() { 11 System.out.println("睡觉觉"); 12 } 13 14 public static void main(String[] args){ 15 Student stu = new Student(); 16 stu.name = "wangyi"; 17 stu.age = 15; 18 System.out.printf("name=%s,age=%d",stu.name,stu.age); 19 stu.eat(); 20 stu.sleep(); 21 } 22 23 }
javac先去编译代码:javac Student.java,然后java虚拟机去执行这个类:java Student,执行代码:
1、加载代码,在方法区生成Student类的相关信息:代码,静态方法,字符串常量等。
2、调用main方法,在栈里面创建一个main方法的栈帧,然后执行Student stu = new Student()语句的左边部分,此时 stu=null 。
3、new Student()则调用类里面的构造方法Student(),此时再栈里面又创建了一个栈帧。
4、在堆里开辟了一片内存存储新建的对象,包括name,age,eat(),sleep(),都是默认值,并将此内存块的地址返回给main方法里面的stu。
5、执行stu.name="wangyi",这是个字符串常量,在方法区里把这个字符串常量的地址给name。
6、对age赋值;
7、调用stu里的eat()方法,打印出方法区里的吃饭饭“”。
8、执行完清除虚拟机内存。
如下示意图。
传智版:
一个对象的内存分析:
对象作为方法参数:
对象作为返回值:
private关键字封装
将属性和方法用private封装后表示,被封装的属性与方法只能在本类中使用,类外部不可见。此时要想访问被封装的属性,必须用getter与setter方法。提高了代码的安全性。
setter方法:主要进行属性内容的设置与修改;
getter方法:主要进行属性内容的取得;
类的设计原则:编写类的时候,没有额外说明,所有属性必须使用private封装(成员变量)
boolean类型的getter取变为isname。
1 int a;//成员变量 2 private int aa = 10; 3 private boolean male; 4 5 6 public void setAaa(int p_a){//存,无返回值,有参 7 aa = p_a; 8 System.out.println("aa的值是 "+ aa); 9 } 10 public int getAa(){//取,有返回值,无参 11 return aa; 12 }
this关键字:
this用来在重名的情况下区分变量。
1 public class This { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 Person person = new Person(); 6 person.name = "wangyi"; 7 person.sayHello("刘亦菲"); 8 9 } 10 } 11 //当方法的局部变量和类的成员变量重名时,根据就近原则,优先使用局部变量。 12 //如果需要访问本类中的成员变量,需要使用:this.成员变量名 13 14 class Person{ 15 String name;//成员变量 16 public void sayHello(String name){//局部变量 17 System.out.println(name + "你好,我是"+ name);//刘亦菲你好,我是刘亦菲 18 System.out.println(name + "你好,我是"+ this.name);//刘亦菲你好,我是wangyi 19 } //这里this.name就是person.name 20 }
构造方法
构造器也叫构造方法(constructor),用于对象的初始化。构造器是一个创建对象时被自动调用的特殊方法,目的是对象的初始化。构造器的名称应与类的名称一致。Java通过new关键字来调用构造器,从而返回该类的实例,是一种特殊的方法。
构造方法一般用来给对象赋初始值;
构造方法的名称必须和所在的类名称完全一致,包括大小写也一样;
构造方法不要写返回值类型,连void都不写;
构造方法不能return一个具体的返回值;
如果没有编写任何构造方法,那么编译器就会默认赠送一个构造方法,没有参数,方法体什么事都不做;
每个类可以有多个构造方法,同名则重载方法。
示例:
1 package day0103; 2 //构造方法 3 4 public class Student { 5 private String name; 6 private int age; 7 8 public Student(){ 9 System.out.println("调用了无参构造方法"); 10 } 11 12 public Student(String name,int age){ 13 System.out.println("调用了全参构造方法"); 14 this.name = name; 15 this.age = age; 16 17 } 18 public void setName(String name){ 19 this.name = name; 20 } 21 public String getName(){ 22 return name; 23 } 24 public void setAge(int age){ 25 this.age = age; 26 } 27 public int getAge(){ 28 return age; 29 } 30 31 32 }
1 public class Damo01Student { 2 3 public static void main(String[] args) { 4 5 System.out.println("hahahaha"); 6 Student stu1 = new Student(); 7 System.out.println(stu1.getName());//null 8 System.out.println(stu1.getAge());//0 9 10 Student stu2 = new Student("wangyi",18); 11 System.out.println(stu2.getName());//wangyi 12 System.out.println(stu2.getAge());//18 13 14 } 15 }
标准的类
一个标准的类通常由下面四个部分组成:
1、所有的成员变量都要使用private关键字修饰;
2、为每一个成员变量编写一对Getter/Setter方法;
3、编写一个无参数的构造方法;
4、编写一个全参数的构造方法;
这样标准的类也叫做Java Bean。
如上面的学生类。
Scanner类
一个可以解析基本类型和字符串的简单文本扫描器。
1 package day0103; 2 3 import java.util.Scanner;//导包 4 5 public class Demo01Scanner { 6 7 public static void main(String[] args) { 8 Scanner sc = new Scanner(System.in);//创建,参数system.in代表从键盘进行输入 9 int num = sc.nextInt(); 10 System.out.println("输入的数字是:"+num ); 11 String str =sc.next(); 12 System.out.println("输入的字符串是:"+str); 13 //键盘输入三个整数,并打印出最大的那个; 14 System.out.println("请输入第一个数字:"); 15 int a = sc.nextInt(); 16 System.out.println("请输入第二个数字:"); 17 int b = sc.nextInt(); 18 System.out.println("请输入第三个数字:"); 19 int c = sc.nextInt(); 20 int temp = a > b ? a : b;//两两相比 21 int max = temp > c? temp : c; 22 System.out.println("输入的最大值是:"+ max); 23 24 } 25 26 }
匿名对象
格式: new 对象名.().属性或方法
只有等号右边的,匿名对象是一次性的,也就是说只能使用一次。
先创建一个类:
1 public class Person1 { 2 String name; 3 public void showName(){ 4 System.out.println("name是:"+ name); 5 } 6 }
调用类:
1 import javax.jws.Oneway; 2 3 public class Demo01Anonymous { 4 5 public static void main(String[] args) { 6 // TODO Auto-generated method stub 7 Person1 per = new Person1(); 8 per.name = "刘亦菲"; 9 per.showName(); 10 System.out.println("============================"); 11 //匿名对象 12 //匿名对象只能使用唯一的一次; 13 new Person1().name = "杨幂"; 14 new Person1().showName();//null 15 } 16 }
匿名对象作为方法的参数和返回值都是可以的。
1 package day0103; 2 import java.util.Scanner; 3 4 public class Demo01Anonymous { 5 public static void main(String[] args) { 6 //匿名对象作为方法的参数和返回值都是可以的 7 //使用一般方法进行传参 8 Scanner sc = new Scanner(System.in); 9 methodParam(sc); 10 //使用匿名对象进行传参 11 methodParam(new Scanner(System.in)); 12 13 //返回值直接调用 14 String string = methodReturn().next(); 15 System.out.println("输入的字符串是:"+string); 16 } 17 public static void methodParam(Scanner sc){//作为参数 18 int num = sc.nextInt(); 19 System.out.println("输入的数字是:"+num); 20 } 21 public static Scanner methodReturn(){//作为返回值 22 return new Scanner(System.in); 23 } 24 }
Random类
Random类用来生成一个随机数。
1 import java.util.Random; 3 public class Dome01Random { 4 5 public static void main(String[] args) { 6 //生成一个随机数 7 Random random = new Random(); 8 int num1 = random.nextInt();// -2^31——2^31-1 9 System.out.println("随机数是:"+ num1); 10 int num2 = random.nextInt(2);//[0,2) 11 System.out.println("随机数是:"+ num2);
猜数字的游戏
规则:猜数字的游戏,写下0-100之内一个数,让别人猜,猜少了或者猜多了都告诉对方,看谁用的次数最少把数字猜中。
1 package day0103; 2 import java.util.Random; 3 import java.util.Scanner; 4 public class Dome01Random { 5 6 public static void main(String[] args) { 7 //生成一个随机数 8 /* Random random = new Random(); 9 int num1 = random.nextInt();// -2^31——2^31-1 10 System.out.println("随机数是:"+ num1); 11 int num2 = random.nextInt(2);//[0,2) 12 System.out.println("随机数是:"+ num2);*/ 13 14 //猜数字的游戏,写下0-100之内一个数,让别人猜,猜少了或者猜多了都告诉对方,看谁用的次数最少把数字猜中 15 16 17 Random random = new Random(); 18 int num = random.nextInt(101);//[0,100] 19 Scanner sc = new Scanner(System.in);//创建键盘输入的对象 20 int i = 0;//计数器 21 while (true){ 22 System.out.println("你猜是几?"); 23 int temp = sc.nextInt(); 24 if(temp>num){ 25 i++; 26 System.out.println("太大了,请重新猜!"); 27 28 }else if(temp<num){ 29 i++; 30 System.out.println("太小了,请重新猜!"); 31 32 }else if(temp==num){ 33 i++; 34 System.out.printf("恭喜你猜中了,总共猜了第%d次",i); 35 break;//猜中游戏结束 36 } 37 } 38 39 } 40 }
对象数组
定义一个数组,用来存储3个Person对象。
首先创建一个标准类:
1 //标准的类 2 public class Person { 3 private String name; 4 private int age; 5 public Person(){ 6 7 } 8 public Person(String name,int age){ 9 this.name=name; 10 this.age=age; 11 } 12 public String getName(){ 13 return name; 14 } 15 public void setName(String name){ 16 this.name = name; 17 } 18 public void setAge(int age){ 19 this.age = age; 20 } 21 public int getAge(){ 22 return age; 23 } 24 }
将对象作为数组的元素:
1 public class Demo01ObjectArray { 2 3 public static void main(String[] args) { 4 // 创建一个长度为3的数组,里面用来存放Person类型的对象 5 //int[] array = new int[3]; 6 Person[] array = new Person[3]; 7 //创建三个对象 8 Person one = new Person("刘亦菲",17); 9 Person two = new Person("杨幂",18); 10 Person three = new Person("高圆圆",19); 11 //将one当中的地址值赋值到数组的0号元素位置 12 array[0] = one; 13 array[1] = two; 14 array[2] = three; 15 System.out.println(array[0]);//day0106.Person@15db9742 16 System.out.println(array[1]); 17 System.out.println(array[2]); 18 19 System.out.println(array[1].getName());//杨幂 20 } 21 }
ArrayList集合
数组有个最大的缺点,就是一旦创建长度就不可变。
java.util.ArrayList是大小可变的数组的实现,存储在内的数称为元素。此类通过一些方法来操作内部存储的元素。ArrayList中可不断添加元素,其大小也自动增长。
对于ArrayList来说,有一个<E>代表泛型,即装在集合当中的所有元素的类型。集合中只能装一种类型,而且是引用类型,不能是基本类型。因为集合里保存的都是地址值,但是基本类型的数据没有地址值。
1 import java.awt.List; 2 import java.util.ArrayList; 3 4 public class Demo02ArrayList { 5 6 public static void main(String[] args) { 7 // 创建一个ArrayList集合,名称是list,装的是字符串类型的元素。 8 ArrayList<String> list = new ArrayList<>(); 9 System.out.println(list);// [],对于AL集合来说,直接打印得到的不是地址值,而是内容 10 //add方法向集合添加数据,末尾追加 11 list.add("王珞丹"); 12 list.add("周冬雨"); 13 list.add("黄圣依"); 14 list.add("李小璐"); 15 System.out.println(list);// [王珞丹, 周冬雨, 黄圣依, 李小璐] 16 //获取元素,根据索引,从0开始 17 String name = list.get(1); 18 System.out.println(name); 19 //删除元素,根据索引,从0开始,remove 20 String RemoveWho = list.remove(3); 21 System.out.println("被删除的人是:"+ RemoveWho); 22 System.out.println(list); 23 //获取集合长度 24 int size = list.size(); 25 System.out.println("集合的长度为:" + size); 26 //遍历集合 27 for (int i = 0; i < list.size(); i++) { 28 System.out.println(list.get(i)); 29 } 30 } 31 }
如果想向集合ArrayList当中存储基本类型数据,必须使用基本类型对应的“包装类”(引用类型,都位于java.lang包下)。比如把“66”这个数字包装成一个对象。
八大基本数据类型对应的包装类:
byte-Byte,short-Short, int-Integer, long-Long, float-Float, double-Double, char-Charcter, boolean-Boolean.
八个中只有两个需要记一下,其他的都是首字母大写就变成了包装类的名字。
3 import java.awt.List; 4 import java.util.ArrayList; 5 6 public class Demo02ArrayList { 7 8 public static void main(String[] args) { 9 //使用包装类将基本类型封装成对象存到集合中。 10 ArrayList<Integer> list1 = new ArrayList<>(); 11 12 //add 13 list1.add(100); 14 list1.add(66); 15 list1.add(520); 16 System.out.println(list1); 17 //get 18 System.out.println(list1.get(1)); 19 } 20 }
练习1:生成6个1-33之间的随机整数,添加到集合,并遍历集合。
1 import java.util.ArrayList; 2 import java.util.Random; 3 4 public class Demo03ArrayListRandom { 5 6 public static void main(String[] args) { 7 //练习:生成6个1-33之间的随机整数,添加到集合,并遍历集合。 8 ArrayList<Integer> list = new ArrayList<>(); 9 Random r = new Random(); 10 for (int i = 0; i < 6; i++) { 11 int num = r.nextInt(33)+1; 12 list.add(num); 13 } 14 for (int i = 0; i < list.size(); i++) { 15 System.out.println(list.get(i)); 16 } 17 } 18 }
练习2:定义以指定格式打印集合的方法。格式:{元素@元素@元素}
1 import java.util.ArrayList; 2 3 public class Demo04PrintArrayList { 4 public static void main(String[] args){ 5 //练习:定义以指定格式打印集合的方法。格式:{元素@元素@元素} 6 ArrayList<String> list = new ArrayList<>();//创建集合对象 7 list.add("张飞");//添加元素 8 list.add("关羽"); 9 list.add("刘备"); 10 list.add("诸葛亮"); 11 12 printAL(list);//调用方法 13 } 14 public static void printAL(ArrayList<String> list){//自定义打印集合方法 15 System.out.print("{");//拼接{ 16 for (int i = 0; i < list.size(); i++) { 17 if(i==list.size()-1){ 18 System.out.print(list.get(i)+"}"); 19 }else{ 20 System.out.print(list.get(i)+"@"); 21 } 22 } 23 } 24 }
练习3:生成10个1-33之间的随机整数,添加到集合,筛选其中的偶数元素,放到另一个集合中,并遍历集合。
1 import java.util.ArrayList; 2 import java.util.Random; 3 4 public class Demo03ArrayListRandom { 5 6 public static void main(String[] args) { 7 //练习:生成10个1-33之间的随机整数,添加到集合,筛选其中的偶数元素,放到另一个集合中,并遍历集合。 8 ArrayList<Integer> list = new ArrayList<>(); 9 Random r = new Random(); 10 for (int i = 0; i < 10; i++) { 11 int num = r.nextInt(33)+1; 12 list.add(num); 13 } 14 15 ArrayList<Integer> list2 = new ArrayList<>();//创建第二个集合存放偶数 16 for (int i = 0; i < list.size(); i++) { 17 if(list.get(i)%2==0){ 18 list2.add(list.get(i)); 19 } 20 } 21 22 for (int i = 0; i < list2.size(); i++) {//遍历输出 23 System.out.println(list2.get(i)); 24 } 25 } 26 }
HashSet
1 import java.util.HashSet; 2 3 public class Demo02HashSet { 4 5 public static void main(String[] args) { 6 // HashSet是无序,不可重复的 7 HashSet<String> set = new HashSet<>(); 8 set.add("wenwen"); 9 set.add("wenwen"); 10 set.add("ziqing"); 11 set.add("yifei"); 12 System.out.println(set); 13 System.out.println(set.size());//3 14 System.out.println(set.contains("yifei"));//true 15 set.remove("wenwen");//移除 16 System.out.println(set.size());//2 17 System.out.println(set.isEmpty());//true 18 for (String string : set) {//遍历 19 System.out.println(string); 20 } 21 set.clear();//清空 22 System.out.println(set.size());//0 23 } 24 }
HashMap
3 import java.util.HashMap; 4 import java.util.Set; 5 6 public class Demo03HashMap { 7 8 public static void main(String[] args) { 9 //HashMap容器以键值对的形式来保存数据,键不可重复,重复时,后者键值对覆盖前者 10 HashMap<String, String> hashMap = new HashMap<>(); 11 hashMap.put("name", "wenwen"); 12 hashMap.put("age", "22"); 13 hashMap.put("gender", "女"); 14 hashMap.put("address", "安徽安庆"); 15 System.out.println(hashMap);//{address=安徽安庆, gender=女, name=wenwen, age=22} 16 System.out.println(hashMap.get("name"));//wenwen 17 System.out.println(hashMap.get("name1111"));//null 18 hashMap.remove("gender"); 19 Set<String> set = hashMap.keySet();//获取所有的key,放到集合里 20 System.out.println(set);//[address, name, age] 21 System.out.println(hashMap.values());//[安徽安庆, wenwen, 22] 22 System.out.println(hashMap.size());//3 23 System.out.println(hashMap.containsKey("age"));//true 24 //遍历键值对 25 for (String key : hashMap.keySet()) { 26 String value = hashMap.get(key); 27 System.out.println(key+"的值是:"+value); 28 // address的值是:安徽安庆 29 // name的值是:wenwen 30 // age的值是:22 31 33 } 34 } 35 36 }
String类
String的创建:
String类代表字符串,Java程序中的所有字符串字面值如“abc”都作为此类的实例实现;
字符串是常量,一旦创建内容不可改变;
字符串是可以共享的;
字符串效果上相当于是char[]字符数组,但是底层原理是byte[]字节数组;
字符串常见的构造方法:
public String():创建一个空白字符串,不含有任何内容;
public String(char[] array):根据字符数组来创建;
public String(byte[] array):根据字节数组来创建;
String str = "hello":直接创建。
1 public class Demo05String { 2 3 public static void main(String[] args) { 4 5 String str1 = new String(); 6 System.out.println(str1);//空的,字符串什么内容都没有 7 8 char[] charArray = {'a','b','c'}; 9 String str2 = new String(charArray); 10 System.out.println(str2);//abc 11 12 char[] byteArray = {97,98,99,100}; 13 String str3 = new String(byteArray); 14 System.out.println(str3);//abcd 15 16 String str4 = "hello"; 17 } 18 }
String的常量池和内存分析:
字符串地址值比较下的内存分析:
equals内容比较
== 是进行对象的地址值比较,如果需要比较字符串的内容,可以用两种方法:
一、public boolean equals(objest obj):参数可以是任何对象,只有参数是一个字符串并且内容相同的才会给true,否则返回false;
注意:
1、任何对象都能用object进行接收;
2、equals方法具有对称性,也就是说a.equals(b)和b.equals(a)的效果一样;
3、如果比较双方有一个常量一个变量,推荐把常量写在前面;
二、public boolean equalIgnoreCase(object obj),这个跟上面一样,但是会区分大小写。如验证码的应用。
1 public class Demo06StringEquals { 2 3 public static void main(String[] args) { 4 // 内容比较 5 String str1 = "zww"; 6 String str2 = "zww"; 7 char[] charArray = {'z','w','w'}; 8 String str3 = new String(charArray); 9 10 System.out.println(str1.equals(str2));//true 11 System.out.println(str1.equals(str3));//true 12 System.out.println(str3.equals("zww"));//true 13 System.out.println("zww".equals(str1));//true 14 System.out.println("Zww".equals(str1));//false 15 //String str4 = null; 16 //System.out.println(str4.equals("zww"));//NullPointerException 空指针异常 17 18 System.out.println("Zww".equalsIgnoreCase(str1));//true 20 } 21 } 22
String获取方法
String当中与获取相关的常用方法有:
public int length():获取字符串当中含有的字符个数,拿到字符串长度。
public String concat(String str):将当前字符串和参数字符串拼接成为返回值新的字符串。
public char charAt(int index):获取指定索引位置的单个字符(索引从0开始)。
public int indexof(String str):查找参数字符串在本字符串当中首次出现的索引位置,如果没有返回-1值。
1 public class Demo07StringGet { 2 3 public static void main(String[] args) { 4 // 字符串获取相关方法 5 int length = "ahdbahbgajgkaks".length(); 6 System.out.println(length);//15 7 8 String str1 = "hello"; 9 String str2 = "world"; 10 String str3 = str1.concat(str2); 11 System.out.println(str1);//hello,不变 12 System.out.println(str3);//helloworld 13 14 char c = str3.charAt(1); 15 System.out.println(c);//e 16 17 int AtWhere = str3.indexOf("or"); 18 int AtWhere2 = str3.indexOf("ord"); 19 System.out.println(AtWhere);//6 20 System.out.println(AtWhere2);//-1 21 } 22 }
String的截取
字符串的截取方法:
public String substring(int index):截取从参数位置一直到字符串末尾,返回新的字符串;
public String substring(int begin,int end):截取中间一段,左闭右开 ,[begin,end);
1 public class DemoSubstring { 2 3 public static void main(String[] args) { 4 String str1 = "helloworld"; 5 System.out.println(str1.substring(5));//world 6 System.out.println(str1.substring(4,7));//owo 7 System.out.println(str1);//原来的字符串永不变,helloworld 8 } 9 }
String的转换
string当中与转换相关的常用方法有:
public char[] tocharArray():将当前字符串拆分成为字符数组作为返回值。
public bytel] getBytes():获得当前字符串底层的字节数组。
pgblic String replace(CharSequence oldString, CharSequence newstring):将所有出现的老字符串替换成为新的字符串,返回替换之后的结果新字符串。
1 public class Demo09StringConvert { 2 3 public static void main(String[] args) { 4 //转换成字符数组 5 char[] chars = "HelloWorld".toCharArray(); 6 System.out.println(chars);//HelloWorld 7 System.out.println(chars.length);//10 8 //转换成字节数组 9 byte[] bytes = "HelloWorld".getBytes(); 10 System.out.println(bytes);//[B@15db9742 11 System.out.println(bytes[1]);//101 12 //字符串的内容替换 13 String str1 = "亚索你会不会玩啊,你大爷的"; 14 String str2 = str1.replace("你大爷", "***"); 15 System.out.println(str1); 16 System.out.println(str2);//亚索你会不会玩啊,***的 17 } 18 }
字符串的切割
切割字符串的方法:
public String[] split(String regex):按照参数的规则,将字符串切成若干个小字符串,返回的是字符串数组。
注意:里面的参数是一个正则表达式,当以英文句点“.”去切割会失败,必须写"\\."
1 public class Demo10StringSplit { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 String str1 = "How are you?"; 6 String[] strings = str1.split(" "); 7 System.out.println(strings);//[Ljava.lang.String;@15db9742 8 for (int i = 0; i < strings.length; i++) { 9 System.out.println(strings[i]); 10 } 11 12 String str2 = "How.are.you?"; 13 String[] strings2 = str2.split("."); 14 System.out.println(strings2);//[Ljava.lang.String;@6d06d69c 15 System.out.println(strings2.length);//长度为0,切割失败 16 String[] strings3 = str2.split("\\."); 17 System.out.println(strings3.length);//3 18 19 } 20 }
字符串练习
练习1:定义一个方法,把数组{1,2,3}按照指定格式拼接成一个字符串。格式: [word1#word2#word3]
1 public class Demo11Exercises { 2 3 public static void main(String[] args) { 4 //定义一个方法,把数组{1,2,3}按照指定格式拼接成一个字符串。格式: [word1#word2#word3] 5 int[] array = {1,2,3}; 6 String string = fromArrayToString(array); 7 System.out.println(string);//[world1#world2#world3] 8 } 9 public static String fromArrayToString(int[] array){ 10 String str = "["; 11 for (int i = 0; i < array.length; i++) { 12 if(i == array.length-1){ 13 str += "world" + array[i] + "]"; 14 }else{ 15 str += "world" + array[i] + "#"; 16 } 17 }return str; 18 } 19 }
练习2:键盘输入一个字符串,并且统计各种字符出现的次数,包括大写字母,小写字母,数字,其他字符
1 import java.util.Scanner; 2 3 public class DemoExercises { 4 5 public static void main(String[] args) { 6 // TODO Auto-generated method stub 7 Scanner sc = new Scanner(System.in); 8 System.out.println("请输入一个字符串:"); 9 String input = sc.next();//获取键盘输入 10 int countUpper = 0;//计数 11 int countLower = 0; 12 int countNumber = 0; 13 int countOther = 0; 14 char[] charArray = input.toCharArray();//将字符串转换为字符数组,这一步很关键 15 for (int i = 0; i < charArray.length; i++) { 16 char ch = charArray[i]; 17 if('A'<=ch && ch<='Z'){ 18 countUpper++; 19 }else if('a'<=ch&&ch<='z'){ 20 countLower++; 21 }else if ('0'<=ch&&ch<='9'){ 22 countNumber++; 23 }else{ 24 countOther++; 25 } 26 } 27 System.out.println("大写字母有:"+ countUpper); 28 System.out.println("小写字母有:"+ countLower); 29 System.out.println("数字有:"+ countNumber); 30 System.out.println("其他字符有:"+ countOther); 31 } 32 }
输入、结果:
Arrays类
java.util.Arrays是一个与数组相关的工具类,里面提供了大量的静态方法,用来实现数组常见的操作。常见的两个方法:
public static String toString(数组):将参数数组变成字符串,按照默认格式:[元素1,元素2]
public static void sort(数组):按照默认升序对数组的元素进行排序。
1 import java.util.Arrays; 2 3 public class Demo03Arrays01 { 4 5 public static void main(String[] args) { 6 //将数组按照默认格式变成字符串 7 int[] array1 = {10,3,6,2}; 8 String str1 = Arrays.toString(array1);//[10, 3, 6, 2] 9 System.out.println(str1); 10 Arrays.sort(array1);//升序排序 11 System.out.println(Arrays.toString(array1));//[2, 3, 6, 10] 12 String[] array2 = {"cc","b","12","%","aaa"}; 13 Arrays.sort(array2); 14 System.out.println(Arrays.toString(array2));//[%, 12, aaa, b, cc] 15 //升序打印出不规则的字符串里的元素 16 String str = "jdahhGHlfaJfagOl19Na"; 17 char[] chars = str.toCharArray();//先把字符串变成字符数组,然后才能用Arrays.sort 18 Arrays.sort(chars); 19 System.out.println(chars);//19GHJNOaaaadffghhjll 20 } 21 }
Math类
java. util.Math类是数学相关的工具类,里面提供了大量的静态方法,完成与数学运算相关的操作。public static double abs(double num):获取绝对值。有多种重载。
public static double ceil (double num):向上取整。
public static double floor(double num):向下取整。
public static Long round(double num):四舍五入。 1 public class Demo04Math {
2 3 public static void main(String[] args) { 4 //数学方法 5 System.out.println(Math.abs(-3.14));//3.14 6 System.out.println(Math.abs(3.14));//3.14 7 System.out.println(Math.abs(0));//0 8 System.out.println(Math.ceil(1.01));//2.0 9 System.out.println(Math.ceil(1.1));//2.0 10 System.out.println(Math.floor(1.9));//1.0 11 System.out.println(Math.round(1.49));//1 12 System.out.println(Math.round(1.5));//2
System.out.println(Math.PI);//3.141592653589793
13 14 } 15 }
练习:
计算在-10.8到5.9之间,绝对值大于6或者小于2.1的整数的个数
1 package day0108; 2 3 4 public class Demo04Math { 5 6 public static void main(String[] args) { 7 8 //计算在-10.8到5.9之间,绝对值大于6或者小于2.1的整数的个数 9 /* 10 int count = 0;//计数器 11 double min = -10.8; 12 double max = 5.9; 13 for (int i = (int) min; i < max; i++) {//强转int 14 int abs = Math.abs(i); 15 if(abs>6 || abs<2.1){ 16 count++; 17 } 18 }System.out.println("总共有:"+count+" 个。"); 19 */ 20 21 int count = 0;//计数器 22 double min = -10.8; 23 double max = 5.9; 24 for (double i = Math.ceil(min); i < max; i++) {//向上取整,double也可以++ 25 double abs = Math.abs(i); 26 if(abs>6 || abs<2.1){ 27 count++; 28 } 29 }System.out.println("总共有:"+count+" 个。");//9 30 31 32 } 33 }
面向对象之继承
继承解决的是共性抽取的问题。父类有的,子类里也要有。
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找。
间接通过成员方法访问成员变量:该方法属于谁,就优先用谁,没有则向上找。
若有三种重名,想访问父类的成员属性,则可以使用关键字super。
创建一个父类:
1 public class Fu {//父类 2 int numFu = 10; 3 int num = 1; 4 5 public void methodFu(){ 6 System.out.println(num); 7 } 8 9 }
创建一个子类:
1 public class Zi extends Fu {//子类 2 int numZi = 20; 3 int num = 2; 4 int numZi2 = 200; 5 6 public void methodZi(){ 7 int num = 2000; 8 System.out.println(num); //2000 局部变量 9 System.out.println(this.num);//2,本类成员变量 10 System.out.println(super.num);//1,父类的成员变量 11 } 12 }
调用:
1 public class Demo05Extend { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 Zi zi = new Zi(); 6 Fu fu = new Fu(); 7 System.out.println(zi.numZi);//20 8 System.out.println(zi.numFu);//10 9 System.out.println(zi.num);//2,优先找子类,没有再去找父类 10 System.out.println(fu.num);//1 11 12 zi.methodFu();//1,调用父类方法,则优先使用父类的num 13 zi.methodZi();//2000,2,1 14 15 } 16 }
方法覆盖重写
方法覆盖重写的注意事项:
1、必须保证父子类之间的方法名称相同,参数列表也相同;
@Override:写在方法前面,用来检测是不是有效的覆盖重写。这个注解可以不写,推荐写上。
2、子类方法的返回值必须小于等于父类方法的返回值范围。
提示:java.lang.Object类是最高父类。
3、子类方法的权限必须大于等于父类方法的权限修饰符。
提示:public > protected > (default) > private, 其中(default)不是关键字default,而是什么都不写,留空。
继承中构造方法的访问
先来个父类:
这是子类:
调用:
结果:
如果:
子类报错:因为默认的super()中并没有传参
子类传参后调用父类重载构造方法:
再去调用:
注意,super的父类构造调用,只能是子类构造方法的第一个语句,调用一次,下面的错误写法:
总结:子类必须调用父类构造方法,不写则赠送super(),写了则用写的指定的super调用,而且super只能有一个,还是在子类构造方法里的第一个语句。
super关键字
super关键字的三种用法总结:
1、在子类的成员方法中访问父类的成员变量;
2、在子类的成员方法中访问父类的成员方法;
3、在子类的构造方法中访问父类的构造方法。
this关键字
this关键字用来访问本类的内容,也有三种用法:
1、在本类的成员方法中访问本类的成员变量;
2、在本类的成员方法中访问本类的另一个成员方法;
3、在本类的构造方法中访问本类的另一个构造方法。
注意:this(...)调用也不许世构造方法的第一个语句;而且super和this不能同时使用。
super,this关键字在继承中的内存分析
先画出内存的三部分,栈,堆和方法区。程序要想运行,方法区里要先有东西,类class的信息都在方法区。方法区里面有Fu类,包括一个成员变量num和一个成员方法method()父,还有Zi类,包括一个成员变量num和一个成员方法method()子,还有个show(),由于Zi类继承自Fu类,所以子类里还有个系统生成的 [supper_class] 标记,这个特殊标记指向了Fu类,就好比是extends关键字。方法区还有个Demo类,里面有个main方法。
接下来main方法率先进栈,在栈中开辟一片内存,首先创建Zi zi 变量,而new Zi()创建的对象都放在堆里面,在堆里画个大方框,里面嵌套一个父类对象,父类外面是子类内容。先有父再有子,父类里有个成员变量num,赋值为10,method父保存的是方法区里的method父的内存地址。外边子类里赋值num为20,method保存的是方法区里的method子的内存地址。还有个show保存的也是地址。此时给外边添加一个this,里面添加一个super关键字,代表各自范围的内容。整个的子类对象在堆中有个地址0x666,这时栈当中Zi zi保存的就是0x666。
往下走,栈里面的main执行zi.show()。show方法是子类独有的方法,此时show进栈,show里面定义了一个变量,是个局部变量,num=30,接着输出num,优先使用局部变量,输出30。然后输出this.num,访问的是本类中的成员变量,20。最后时出书super.num,访问的是父类中的num,输出10。
最后main方法里又执行了zi.method(),优先执行子类中的method,所以子类的method进栈。子method里面执行super.method(),调用的是父类的method方法,这时父类的method进栈。父类method执行完毕后出栈,下面的也依次出栈,内存清空。
抽象类和抽象方法
什么是抽象类:
有抽象方法的类就是抽象类,抽象方法在void前加上abstract关键字,并去掉[]方法体。可见抽象方法无方法体。在class前面也要加上关键字abstract。
抽象类的继承:
Dog类继承了Animal抽象类,必须重写所有的抽象方法。
抽象类中也可以有构造方法,供子类创建对象时,初始化父类成员使用的。
抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类。未包含抽象方法的抽象类,目的是不想让调用者创建该类的对象,通常用于某些特殊的类结构设计。
抽象类的子类必须重写抽象父类中所有的抽象方法,除非该子类也是抽象类。
哈士奇中必须要重写所有的抽象方法。假设不重写所有的抽象方法,那么创建对象后调用没有方法体的抽象方法就没有意义了。
接口
定义
Java中的接口就是多个类的公共规范。
接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。
如何定义一个接口呢?
格式:
public interface 接口名称{
接口内容
}
备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class
如果是java 7,那么接口中可以包含的内容有:
1、常量;
2、抽象方法;
如果是Java 8,额外包含:
3、默认方法;
4、静态方法;
如果是Java 9,还额外包含:
5、私有方法;
注意:接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract,不能是其他的如pravite,
并且这两个关键字可以选择性的省略。
接口使用步骤:
1、接口不能直接使用,必须要有一个“实现类”来“实现”该接口;
格式:
public class 实现类名称 implement 接口名称{ 。。。 }
2、接口的实现类必须覆盖重写接口中所有的抽象方法。即去掉abstract关键字,加上{}。
3、创建实现类的对象,进行使用。
这是一个接口:
这是接口的实现类:
调用:
注意:如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己必须是个抽象类。 跟继承一样。
接口中默认方法
当接口升级时新添加了一个抽象方法,那么之前的实现类就都会报错。这种情况下就该在接口中定义一个默认方法而不是抽象方法。从Java8
开始,接口允许定义默认方法。
默认方法不需要在实现类中覆盖重写,直接在实现类的对象里调用即可。
当然,实现类也可以覆盖重写接口中的默认方法,根据需要而定。
接口中静态方法
接口中的静态方法定义很简单,就是public static
静态方法跟对象无关,所以直接通过接口名调用:
接口中常量
接口中也可以定义【成员变量】,但是必须使用public static final三个关键字(可省)修饰,从效果上看,这其实就是接口的【常量】
注意:一旦使用final关键字进行修饰,就不可改变了。所以在定义的时候就要给它赋值。而且常量名称要大写,用下划线连接。
接口使用注意事项:
1、接口是没有静态代码块或构造方法的;
2、一个类的直接父类是唯一的,但是一个类可以同时实现多个接口;
格式:public class Zi extends Fu implements MyInterfaceAbs,MyInterfaceAbs2{}
3、如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可;
4、如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类;
5、如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写;
6、一个继承于父类的子类,其父类当中的方法和接口当中的默认方法重名时,优先用父类当中的方法。
接口多继承
1、类与类之间是单继承的,一个类的直接父类只有一个;
2、类与接口之间是多实现的,一个类可以实现多个接口;
3、接口与接口之间是多继承的。
4、多个父接口当中的抽象方法如果重复,没关系;
5、多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,必须带上default关键字
面向对象之多态
面向对象的三大特征:封装性、继承性、多态性。
extends继承或者implements实现是多态性的前提。
比如人类这个父类,学生继承了人类,学生类是人类的一个子类,学生小明是学生类的对象,小明既是一个学生(学生形态),也是一个人(人类形态)。一个对象拥有多种形态,这就是对象的多态性。
代码中体现多态性,其实就是一句话:父类引用指向子类对象。
格式:父类名称 对象名 = new 子类名称();
或者:接口名称 对象名 = new 实现类名称();
怎么理解 Class cla = new Class();?
cla变量存放的是类Class的实例化对象在系统中的内存地址。
成员变量是不能进行覆盖重写的,只有方法可以。
访问成员变量的两种方式:
1、直接通过对象名称访问成员变量:看等号左边是谁就优先用谁,没有则向上找;
2、直接通过成员方法访问成员变量:看方法属于谁,优先用谁,没有则向上找。
结果:
访问成员变量:编译看左边,运行也看左边;
访问成员方法:编译看左边,运行看右边;
多态的好处:
未完待续。。。
a
a