Java基础
数据类型扩展及面试题讲解
整数拓展: 进制 、 二进制0b 、 十进制 、 八进制0 、 十六进制0x
浮点数拓展:银行业务怎么表示?钱 —— 最好完全避免使用浮点数进行比较
使用 BigDecimal 数学工具类
- float:有限、离散、舍入误差、大约、接近但不等于
- double:精度问题
字符拓展:所有的字符本质皆数字。
- 编码:Unicode 、 2字节 、 0-65535 、 Excel 、 2^16=65536
- U0000-UFFFF,如‘ \u0061’,就是a
类型转换
低 ----------------------------------------------------------> 高
byte, short, char → int → long → float → double
JavaDoc生成文档
红框内是为了防止中文乱码
Java流程控制
Scanner
实现程序和人的交互
- 创建对象
Scanner s = new Scanner(System.in)
- 获取输入的字符串
s.next() | s.nextLine()
- 判断是否有输入的数据
s.hasNext() | s.hasNextLine()
- 关闭
s.close()
注:
- next():
- 一定要读到有效字符后才可以结束输入
- 对有效字符之前遇到的空白,自动去掉
- 只有输入有效字符后才将其后面输入的空白作为分隔符或结束符
- 不能得到带有空格的字符串
- nextLine():
- 以Enter为结束符,遇到Enter前的所有字符都返回
- 可以获得空白
输入特定类型的数据
//判断输入是否为整数
Scanner s = new Scanner(System.in);
int i=0;
System.out.println("请输入整数:");
if(s.hasNextInt()){
i = s.nextInt();
System.out.println("整数为"+i);
}else{
System.out.println("输入非整数");
}
scanner在输入的时候,如果是接连两个s.nextXXX()
连着用,第一个如果不符合类型,第二个符合,则直接赋值
增强For循环
语法格式:
for(声明语句 : 表达式)
{
//代码句子
}
//遍历数组的元素
int[] numbers = {10, 20, 30, 40, 50};
for (int x: numbers){
System.out.println(x);
}
Java方法
方法重载
方法名相同,参数不同,则可以说一个方法是另一个方法的重载
- 方法名必须相同
- 方法的参数类型,参数个数不一样
- 方法的返回类型可以不相同
- 方法的修饰符可以不相同
- main方法也可以被重载
可变参数
传递同类型的可变参数给一个方法。在方法生命中,在指定参数类型后加一个省略号(…)
public static void test(double... numbers) {
for(int i=0;i< numbers.length; i++){
System.out.println(numbers[i])
}
}
//调用
test(1,2,3,4,5);
test(new double[]{1,2,3,4,5});
一个方法中只能指定一个可变参数,它必须是方法中的最后一个参数。任何普通的参数必须在它之前声明。
public void test(int x, int... i){
}
★递归
递归包含两个部分
递归头:什么时候不用调用自身方法。如果没有头,将陷入死循环。
递归体:什么时候需要调用自身方法。
Java数组
数组声明&创建
-
定义/声明数组变量
dataType[] array; //首选 or dataType array[]; //效果相同,并非首选,在c、c++中使用
-
创建数组/初始化
dataType[] array = new dataType[arraySize];
三种初始化&Java内存分析
java内存分析
- 堆:存放new对象和数组;可以被所有的线程共享,不会存放别的对象引用
- 栈:存放基本变量类型(会包含这个基本类型的具体数值);引用对象的变量(会存放这个引用在堆里面的具体地址)
- 方法区:可以被所有的线程共享;包含了所有的class和static变量
三种初始化状态
-
静态初始化(定义之后大小不可改变)
//基本类型 int[] a ={1,2,3}; //引用类型 Man men = {new Man(1,1), new Man(2,2)}; pulic class Man(int a, int b){ ... }
-
动态初始化
int[] a = new int[2]; a[0]=1; a[1]=2;
-
数组的默认初始化
- 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
下标越界
- 数组长度是确定的,一旦被创建,大小不可改变
- 元素为相同类型,且元素类型可以是基本类型和引用类型
- 数组变量属于引用类型,数据可以看作对象,数组中的每个元素相当于该对象的成员变量
- 数组本身就是对象,Java的对象在堆中,因此数组无论保存原始数据还是其他对象类型,数组对象本身是在堆中的。
数组下标越界异常:ArrayIndexOutOfBoundsException
数组的使用(进阶)
增强型for循环+数组
int[] a ={1,2,3,4};
for(int array: a){
System.out.println(array)
}
数组作为方法参数
public static void main(String[] args){
int[] a ={1,2,3,4};
printArray(a);
}
public static void printArray(int[] arrays){
for(int i=0;i<arrays.length;i++){
System.out.println(arrays[i]+" ")
}
}
数组作为返回值
public static void main(String[] args){
int[] a ={1,2,3,4};
int[] r = reverse(a);
printArray(r);
}
//反转数组
public static int[] reverse(int[] arrays){
int[] result = new int[arrays.length];
for(int i=0, j=result.length-1; i<arrays.length; i++,j--){
result[j] = arrays[i];
}
return result;
}
二维数组
格式:int[][] a = new int[2][5]
//静态初始化
int[][] array = {{1,2},{3,4},{5,6}}
Arrays类
- 打印数组元素:
Arrays.toString(a)
- 数组排序:
Arrays.sort(a)
,升序 - 数组赋值:
Arrays.fill(a,0)
… - 比较数组:equals()
- 查找数组元素:binarySearch()(对有序数组进行二分查找)
稀疏数组
处理方式:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模(三元组)
public class sparseArray {
public static void main(String[] args) {
//1、创建一个二维数组 11*11 0:没有棋子, 1:黑棋子 2:白棋子
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 1;
System.out.println("输出原始的数组");
for(int[] ints : array1){
for(int anInt : ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
//转换为稀疏数组
//获取有效值的个数
int sum = 0;
for(int i = 0; i < 11;i++){
for(int j = 0; j < 11; j ++){
if(array1[i][j] != 0){
sum++;
}
}
}
System.out.println("有效值的个数:"+sum);
//2、创建一个稀疏数组的数组
int[][] array2 = new int[sum + 1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;
//遍历二维数组,将非0的值存放到稀疏数组中
int count = 0;
for(int i =0; i < array1.length;i++){
for(int j=0; j<array1[i].length;j++){
if (array1[i][j]!=0){
count++;
array2[count][0] = i; //第一个位置存行数
array2[count][1] = j; //第二个位置往列数
array2[count][2] = array1[i][j]; //第三个位置存放元素值
}
}
}
//输出稀疏数组
System.out.println("稀疏数组");
for(int i=0;i<array2.length;i++){
System.out.println(array2[i][0]+"\t"
+array2[i][1]+"\t"
+array2[i][2]);
}
System.out.println("================");
//稀疏数组还原
System.out.println("稀疏数组还原");
//1、读取稀疏数组值
int[][] array3 = new int[array2[0][0]][array2[0][1]];
//2、给其中的元素还原值
for(int i=1;i < array2.length ; i++){
array3[array2[i][0]][array2[i][1]] = array2[i][2];
}
//3、打印
for(int[] ints: array3){
for(int anInt: ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
}
}
★Java面向对象OOP
面向对象
面向过程(线性思维)
- 步骤清晰简单,第一步做什么,第二步做什么……
- 面对过程适合处理一些较为简单的问题
面向对象
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后才对某个分类下的细节进行面向过程的思索
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个儿系统。但是,具体到围观操作,仍然需要面向过程的思路去处理。
面向对象的本质:以类的方式组织代码,以对象的形式封装数据
抽象
三大特定:封装、继承、多态
方法
break和return的区别
- break:跳出switch,结束循环
- return:结束方法
调用
值传递
引用传递
属性
属性默认初始化
- 数字: 0 0.0
- char:u0000
- boolean:false
- 引用:null
类与对象的关系
类:抽象的数据类型,是对一类事物整体描述/定义,但是并不能代表某一个具体的事物,如Person类
对象:抽象概念的具体实例,如张三
一个项目应该只存在一个main方法,所以不是所有类里都有main方法
构造器
一个类即使什么都不写,它也会存在一个方法,即为构造方法(快捷键:alt+ins/insert)
- 必须与类名相同
- 必须没有返回类型,也不写void
构造器的作用:
- 实例化初始值
- 使用new关键字,本质是在调用构造方法
- 用来初始化对象的值
public class Person {
// 一个类什么也不屑,他也会存在一个方法
// 显示的定义构造器
String name;
// 构造方法,无参构造 aalt+ins-> select none
public Person(){
this.name = "rohal";
}
// 有参构造:一旦定义了有参构造,无参构造就必须显式定义
// 即,必须有上面的Person()方法,否则报错
public Person(String name){
// this代表当前对象
this.name = name;
}
}
封装
程序设计追求“高内聚,低耦合”。
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合:仅暴漏少量的方法给外部使用
封装(数据的隐藏),通常应该禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
即,属性私有,get/set,快捷键alt+ins
意义:
- 提高程序安全性,保护数据
- 隐藏代码实现细节
- 统一接口
- 提高系统可维护性
继承
继承的本质是对一批类的筹项,从而实现对现实世界更好的建模
extends的意思是”扩展“,子类是父类的继承
Java类中只有单继承,没有多继承
继承是类与类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等。
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,extends
object类
java中所有的类都默认继承Object类。
public class Person{}
等价于
public class Person extends Object{}
Super详解
- super是调用父类的构造方法,必须在子类构造方法的第一个
- super必须只能出现在子类的方法或构造方法中!
- super和this不能同时调用构造方法(因为都要放在第一行,会出错)
exp1.
exp2.
VS this:
- 代表对象不同:
- this:本身调用者这个对象
- super:代表父类对象的应用
- 前提:
- this:没有继承也可以使用
- super:只能在继承条件下才可以使用
- 构造方法:
- this():默认本类的构造
- super():默认父类的构造
方法重写
前提:需要有继承关系,子类重写父类的方法!(若子类重写了父类的方法,则调用子类的,否则调用父类的)
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大但不能缩小: public>protected>default>private
- 抛出的异常:范围可以被缩小但不能扩大:ClassNotFoundException ——> Exception(大)
重写,子类的方法和父类必须要一致;方法体不同!
为什么需要重写?
- 父类的功能,子类不一定需要,或者不一定满足!
- Alt + Insert ; override
静态方法:方法的调用只和左边/定义的数据类型有关。
非静态方法:重写,这里的B b = new A()是子类重写了父类的方法
多态
实现程序的动态编译,通过多态可以让程序的可扩展性更强
- 即同一方法可以根据发送对象的不同而采用多种不同的行为方式
- 一个对象的实际类型是确定的,但是可以指向对象的引用类的有很多(父类、有关系的类)
注意:
- 多态是方法的多态,属性没有多态
- 父类和子类,有联系 类型转换异常:ClassCastException!
- 多态存在的条件:
- 有继承关系
- 子类重写父类方法 (除static 方法 不属于实例,final 常量,private 私有类)
- 父类引用指向子类对象 Father f1 = new Son();
public class Person {
public void run(){
System.out.println("run");
}
}
public class Student extends Person {
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的
// new Student;
// new Person;
// 可以指向的引用类型就不确定了:父类的引用指向子类
// Student 能调用的方法都是自己的或者继承父类的
Student s1 = new Student();
// Person父类型,可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student();
Object s3 = new Student();
s1.run(); //输出:son
s2.run(); //输出:son
// 对象能执行哪些方法,主要看对象左边的类型,与右边关系不大!
s2.eat();//此处报错,子类重写了父类的方法,执行子类的方法
s1.eat();//输出:eat
}
}
instanceof:引用类型之间的转换
可以判断两个类之间是否存在父子关系: System.out.println(X instanceof Y);
能不能编译通过取决于X是不是Y的孩子
public class Person {
public void run(){
System.out.println("run");
}
}
public class Student extends Person {
}
public class Teacher extends Person {
}
public class Application {
public static void main(String[] args) {
//Object > String
//Object > Person > Teacher
//Object > Person > Student
Object object = new Student();
System.out.println(object instanceof Student); //true
System.out.println(object instanceof Person); //true
System.out.println(object instanceof Object); //true
System.out.println(object instanceof Teacher); //false
System.out.println(object instanceof String); //false
Person person = new Person();
System.out.println(person instanceof Student); //true
System.out.println(person instanceof Person); //true
System.out.println(person instanceof Object); //true
System.out.println(person instanceof Teacher); //false
// System.out.println(person instanceof String); //编译报错
Student student = new Student();
System.out.println(student instanceof Student); //true
System.out.println(student instanceof Person); //true
System.out.println(student instanceof Object); //true
// System.out.println(student instanceof Teacher); //编译出错
// System.out.println(student instanceof String); //编译报错
}
}
编译看左,运行看右。
- 子类转换为父类,子类拓展的方法就会消失。向上转型,不用强制转换。
- 父类转子类,向下转型,强制转换。
- 方便方法的调用,减少重复的代码!简洁
public class Application {
public static void main(String[] args) {
//类型之间的转化:父 子
//高 低
Person obj = new Student();
// obj.go();//出错,因为go是孩子类 Student 里的方法
//将这个对象转换为Student类型,就可以使用Student类型的方法(高->低,强制类型转换)
Student student = (Student) obj;
student.go();
//或者
((Student)obj).go();
//子类转换为父类,可能丢失自己的本来的一些方法!
Person person = student;
person.go();//报错
}
}
static关键字
静态变量对于类而言在内存中只有一个,所有对象(实例)所共享,当直接使用类去调用得到说明这个变量是静态的。
只执行一次。
public void run(){
go();//非静态可以调用静态方法
}
public static void go(){
}
public static void main(String[] args){
new Student().run();
Student.go();
go();
}
静态代码块只执行一次
静态导入包
import static java.lang.Math.random;
Math.random()
抽象类
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
抽象的抽象:约束
子类继承抽象类,那么就必须要实现抽象类没有实现的筹项方法,否则该子类也要声明为抽象类
//abstract 抽象类本质:类 需extends,just单继承,how多继承->接口
public abstract class Action {
//约束~有人帮我们实现
//abstract,抽象方法,只有方法名,没有方法的实现。
public abstract void doSomething();
}
//抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非子类也是abstract,则没必要实现
public class A extends Action {
@Override
public void doSomething() {
}
}
接口
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范!
作用:
- 约束
- 定义一些方法,让不同的人实现
- public abstract
- public static final
- 接口不能被实例化,因为接口中没有构造方法
- 接口可以实现多个,需要implements,必须要重写接口中的方法
接口即规范,定义的是一组规则,体现了现实世界中”如果你是…则必须能…“的思想。如:如果你是汽车,则必须能跑
接口的本质是契约,例如人要遵循的法律。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。
声明类的关键字:class;声明接口的关键字:interface
//interface 定义的关键字,接口都需要有实现类
public interface UserService {
//接口中的所有定义都是抽象的 public
void add(String name);
void update(String name);
void delete(String name);
void query(String name);
}
public interface TimeService {
void timer();
}
//抽象类: extends
//类 可以实现接口 implements 接口
//实现了接口的类,就需要重写接口中的方法
//多继承,利用接口实现多继承
public class UserServiceImpl implements UserService, TimeService{
@Override
public void add(String name) {
}
@Override
public void update(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
内部类
成员内部类
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
}
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通过外部类实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
}
}
静态内部类
public class Outer {
private static int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
//静态内部类无法访问非静态的属性
public static class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//外部类写法的id拿不到了,因为是静态内部类,所以id也应该定义为静态的
//这里主要是因为加到内存中的顺序不同
public void getID(){
System.out.println(id);
}
}
}
拓展
public class Outer {
}
//一个java类中可以有多个class类,但是只能有一个public class
class A{
}
局部内部类
public class Outer {
public void method(){
class Inner{
public void in(){
}
}
}
}
匿名内部类
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
UserService userService = new UserService(){
@Override
public void hello(){
}
};
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}
异常机制Exception
Error 和 Exception
检查性异常:用户错误或问题引起的异常,程序员无法预见。
运行时异常:可能被程序员避免,可以在编译时被忽略。
错误ERROR:≠异常,是脱离程序员控制的问题,在代码中通常被忽略。如:栈溢出。
异常的超类:java.lang.Throwable
- Error:java虚拟机生成并抛出,与编写者无关
- VirtulMachineError:java虚拟机运行错误
- A WTError
- Exception:异常,一般由程序逻辑错误引起
- IOExcetion
- RuntimeException:运行时异常
- ArrayIndexOutOfBoundsException:数组下标越界
- NullPointerException:空指针
- ArithmeticException:算数异常
- MissingResourceException:丢失资源
- ClassNotFoundException:找不到类
捕获和抛出异常
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
try { //try监控区域
System.out.println(a/b);
}catch(ArithmeticException e){//捕获异常,括号中为异常的类型,最高为Throwable
System.out.println("程序出现异常,变量b不能为0");
}finally{//处理善后工作,是否出异常都会执行
System.out.println("finally");
}
//finally可以不要,假设IO流或与资源相关的,需要关闭,这些操作可以写在finally里面
}
}
public static void main(String[] args) {
int a = 1;
int b = 0;
//假设捕获多个异常:从小到大!
try { //try监控区域
System.out.println(a/b);
}catch(Error e){
System.out.println("Error");
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable t){//最大的写最后
System.out.println("Throwable");
}
finally{//处理善后工作,是否出异常都会执行
System.out.println("finally");
}
}
选中代码,ctry+alt+t,快捷键
try {
System.out.println(a/b);
} catch (Exception e) {
e.printStackTrace();//打印错误的栈信息
} finally {
}
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
new Test().test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
//假设这方法中,处理不了这个异常,在方法上抛出异常
public void test(int a, int b){
if(b==0){ //throw throws
throw new ArithmeticException();//主动抛出异常,一般在方法中使用
}
}
自定义异常及经验
用户自定义异常只需继承Exception类即可
步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常
//自定义异常类
public class MyException extends Exception{
//传递数字 >10
private int detail;
public MyException(int a) { //3
this.detail = a;
}
//toString:异常的打印信息
@Override
public String toString() {
return "MyException{"+detail+'}';//4
}
}
public class Test {
//可能会存在异常的方法
static void test(int a) throws MyException{
System.out.println("传递的参数为"+a);
if (a>10){
throw new MyException(a); //1、抛出异常
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) { //2、捕获异常
//增加处理异常的代码
System.out.println("MyException=>"+e);
}
}
}
总结:
- 处理运行异常时,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加finally语句块去释放占用的资源,IO,Scanner
<details>
<summary>点击查看代码</summary>
</details>
标签:int,void,System,笔记,学习,println,JavaSE,public,out
From: https://www.cnblogs.com/rohal/p/17649423.html