一头扎进 JAVA

硅不可
吉米html

JAVA 基础 -- 基础不牢,地动山摇

子类应该比 父类更为 开放 (public protected default private)
子类方法不能比父类抛出更高异常( 能够为父类方法异常自己或者其异常的子类)

关于类加载和初始化相关的案例
总的顺序是:先父类后子类,先静态后动态,属性和代码块的初始化遵循正常的出场顺序不管是静态仍是动态,可是他们老是先于构造器执行。可是仍是须要经过题目的学习来加深咱们的理解。

package ooptest;
 
public class StaticDemo6 {
 
    public static void main(String[] args) {
        new SB();
    }
    
 
}
 
class SA {
 
    D d;
 
    static {
        System.out.println("A 1"); // 1.先从父类的静态开始
    }
 
    {
        System.out.println("A 2"); // 5.此处开始new对象(非静态相关)
        d = new D();// 6.顺序执行
    }
 
    public SA() {
        System.out.println("A 3"); // 10.此时调用完了本身的非静态代码块来到了构造器
    }
 
}
 
class SB extends SA {
    static C c = new C(); // 2.调用完了父类的静态相关来到子类的静态相关
 
    static {
        System.out.println("B 1"); // 4.接着按照顺序来调用本身的静态代码块 ,到此子类的全部静态都执行完毕接下来将会执行非静态相关
    }
 
    {
        System.out.println("B 2"); // 11.父类的构造器调用完成调用子类的非静态块
    }
 
    public SB() {
        System.out.println("B 3"); // 12.调用完了本身的非静态块调用本身的构造方法 
    }
 
}
 
class C {
    public C() {
        System.out.println("C"); // 3.C没有父类与静态直接调用本身的构造器  // 8.
    }
}
 
class D extends C {// 7. 来到了D可是D有本身的父类因此到达C类
    public D() {
        System.out.println("D");// 9.调用完了父类的构造器会来到子类的构造器
    }
}




package com.ghc.mmall.concurrency.test;


class ClassA {

    public static  ClassA classa = new ClassA();

    static{
        System.out.println("ClassA的静态代码块");
    }

    public ClassA(){
        System.out.println("ClassA的构造方法");
    }

    {
        System.out.println("ClassA的构造代码块");
    }

}

class ClassB extends ClassA{


    static{
        System.out.println("ClassB的静态代码块");
    }

    public ClassB(){
        System.out.println("ClassB的构造方法");
    }

    {
        System.out.println("ClassB的构造代码块");
    }

    public static  ClassC classc = new ClassC();

    public void excute(){
        System.out.println("执行方法");
    }

}

class ClassC {

    public ClassC(){
        System.out.println("ClassC的构造方法");
    }

}

public class Test {

    static {
        System.out.println("Test的静态代码块");
    }

    public static void main(String[] args) {
        System.out.println("执行main方法");
        ClassB b = new ClassB();
        b.excute();
    }

}


能够单步调试 看输出打印结果
Test的静态代码块
执行main方法
ClassA的构造代码块
ClassA的构造方法
ClassA的静态代码块
ClassB的静态代码块
ClassC的构造方法
ClassA的构造代码块
ClassA的构造方法
ClassB的构造代码块
ClassB的构造方法
执行方法



深度加载知识
不管如何类的加载都

①先进行解析(也就是声明静态变量可是不去初始化),也就是将静态变量放入方法区而且标记,标记一个值0。至关于只定义没有赋值。

②当全部的解析都过去的时候才进行初始化,初始化就是按照出场顺序来执行静态代码块和检查静态变量那里是否赋值值,若是有值得话那么就赋值,没有的话那么就将标记值赋值给静态变量。

注意:标记状态的值至关于无值它不能够直接参加运算可是能够间接的使用标记的值。类名调用。


class Demo {
    public static void main(String[] args) {
        System.out.println(Demo.i);
    }
    static {
        i = 7; // 1
    }

    static int i; // 2
}
结果是 7
若是没有定义环境变量classpath,java启动jvm后,会在当前目录下查找要运行的类文件;

若是指定了classpath,那么会在指定的目录下查找要运行的类文件。

还会在当前目录找吗?两种状况:

  1):若是classpath的值结尾处有分号,在具体路径中没有找到运行的类,会默认在当前目录再找一次。
   2):若是classpath的值结果出没有分号,在具体的路径中没有找到运行的类,不会再当前目录找。 
   通常不指定分号,若是没有在指定目录下找到要运行的类文件,就报错,这样能够调试程序。

**级别从低到高为:**byte,char,short(这三个平级)–>int–>float–>long–>double

Java语言规范建议按如下顺序列出修饰符:   
 1. Annotations    2. public    3. protected    4. private    5. abstract    6. static    7. final    8. transient    9. volatile    10. synchronized    11. native    12. strictfp
// 短路与非短路 验证
public class Hello {

    public static boolean compare(int n1, int n2){
        System.out.println(n1+" --> "+n2);
        return n1>n2;
    }

    public static void main(String [] args){
        System.out.println(compare(2,3) & compare(4,3)); // 非短路
        System.out.println(compare(2,3) && compare(4,3)); // 短路
    }
}

产生某个区间内的 随机数

package com.ghc.test;

import java.security.SecureRandom;
import java.util.Random;
import java.util.logging.Logger;
import java.util.logging.Level;
public class Test {
    private static final Logger logger = Logger.getGlobal();
    public static void main(String [] args){
        logger.info("start log info...");
        double res = getRand(1,10);
        double res2 = getRand2(11,20);
        System.out.println(res2);
        double secureRes = getSecureRand(20,30);
        System.out.println(secureRes);
    }
    public static double getRand(long min, long max){
        long gap = max - min;
        return (long)(Math.random() * gap + min);
    }

    public static double getRand2(int min, int max){
        final Random random = new Random();// 默认种子值为 时间戳
        int randInt = random.nextInt(max);
        double res = randInt<min?randInt+min:randInt;
        return res;
    }
    public static double getSecureRand(int min, int max){
        SecureRandom secureRandom = new SecureRandom();
        int randInt = secureRandom.nextInt(max);
        int res = randInt<min?randInt+min:randInt;
        return res;
    }
}

字符串

String s = new String(new char[]{'h','e','l','l'}); 
        System.out.println(s.hashCode()); // 3198781
        String s2 = s.toUpperCase(); 
        System.out.println(s2.hashCode()); // 2213693
        System.out.println(s == s2); // false
        System.out.println(s.equalsIgnoreCase(s2)); //true
从表面上看,两个字符串用==和equals()比较都为true,但实际上那只是Java编译器在编译期,
会自动把全部相同的字符串看成一个对象放入常量池,天然s1和s2的引用就是相同的。

String  的 getBytes 方法 等同于 python 的 encoding , 而 new String("中文","UTF-8") 就是 decode 了

java 的 日志最经常使用的 仍是 org.apache.commons.logging

在 开发阶段不须要引入 Log4j , 尽管 Log4j 是最流行的日志框架,可是使用 commons logging 可以很是容易地切换日志框架
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

只有当咱们要对 log4j 的接口进行扩展时候才须要引入 log4j ,不然 使用 commons logging 足矣
import org.apache.log4j.Logger;

泛型

泛型通常应用于 集合类型中,若是咱们不使用泛型则会变成 Object ,在源码编写时候须要强转,虽然编译后泛型也会被擦除,可是在源码编写时后咱们不须要强转

 E — Element,经常使用在java Collection里,如:  List<E>,Iterator<E>,Set<E>
 K,V — Key,Value,表明Map的键值对
 N — Number,数字
 T — Type,类型,如String,Integer等等

// 由于泛型在编译后就会被擦除, 因此没法被直接实例化
class GenericsA<T>
{
    T t = new T(); // Error
}

type erasure 泛型擦拭 ,全部泛型最终在编译器内都认为是 Object ,因此基本数据类型不能用做泛型,编译后的字节码中不包含任何泛型信息是 Object 而后 (String) obj 这样强转
因此咱们也不能用 p isinstanceof Pair<Integer>.class  或者 用  new T() 这样的方式来编码。





扔掉引用意味着 安全



package com.ghc.test;

import org.junit.Test;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class Pair <T extends Number> {
    private T first;
    private T last;
    Pair(T first, T last){
        this.first = first;
        this.last = last;
    }
    public Pair(){super();}
    public void setFirst(T first){
        this.first = first;
    }

    public void setLast(T last){
        this.last = last;
    }

    public T getFirst(){
        return this.first;
    }
    public T getLast(){
        return this.last;
    }

    public static <K extends Number> Pair createPair(K k1, K k2){
        return new Pair<K>(k1,k2);
    }
    @Override
    public String toString(){
        return "Pair("+this.getFirst()+","+this.getLast();
    }
    @Test
    public void test(){
        Pair<Integer> integerPair = new Pair<>(1,2);
        Pair<Float> floatPair = new Pair<>(1f,2f);

        System.out.println(integerPair.getFirst());
        System.out.println(integerPair.getClass() == Pair.class);
        System.out.println(floatPair.getClass() == Pair.class);

        // 反射 获取 父类泛型
        Class<IntegerPair> clazz = IntegerPair.class;
        Type t = clazz.getGenericSuperclass();
        if(t instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) t;
            Type [] types = parameterizedType.getActualTypeArguments();
            for(Type type:types){
                System.out.println(type.getTypeName());
            }
        }

        String[] results = getSuperClassGenericType(IntegerPair.class);
        for(String res:results){
            System.out.println(res);
        }
    }

    public static<C> String [] getSuperClassGenericType(Class<C> clazz){
       Type superType = clazz.getGenericSuperclass();
       List<String> typesStrList = new ArrayList<>(1);
       if(superType instanceof ParameterizedType){
           Type[] types = ((ParameterizedType) superType).getActualTypeArguments();
           for(Type type:types){
               typesStrList.add(type.getTypeName());
           }
       }
       String [] results = new String[typesStrList.size()];
       return typesStrList.toArray(results);
    }
}

class IntegerPair extends Pair<Integer>{

}
// 调用时候 修改方法签名 ? extends Number
class PairHelper{
    static int add(Pair<? extends Number> p){
        // 在 get 时候 能够变得灵活易用
       Number first =  p.getFirst();
       Number last = p.getLast();
        // p.setFirst(first); extends 上界通配符 容许调用 get 方法,却不容许 调用 set 方法 除了 null
       return first.intValue() + last.intValue();
    }

/*    static int add(Pair<? super Integer> p){
        // 这样 泛型擦除后 就会 是一样的函数了
    }*/
static void set_super(Pair<? super Integer> p){
    // 在 set 时候 能够 变得灵活易用
    //Integer integer = p.getFirst(); // 下边界的 泛型 没法使用 get 仅仅能够用 Object 接收 可使用 set 具体缘由看函数签名
    Object integer = p.getFirst();
    /*Incompatible types.
    Required:
    java.lang.Integer
    Found:
    capture<? super java.lang.Integer>*/
    p.setFirst(Integer.valueOf(2));// Integer 的超类确定能够接收一个 Integer 对象
    }
}

// 最后来一个综合例子 就能够理解 了

class MyCollections{

    public static <T> void copy(List<? extends T> src , List<? super T> dest){
        for(T t:src){
            dest.add(t);
        }
    }
}

枚举类型 本质是 enum class

public enum WeekDayEnum {
    MONDAY("monday",1),
    TUESDAY("tuesday",2),
    WEDNESDAY("Wednesday",3),
    THURSDAY("thursday",4),
    FRIDAY("friday",5);
    private String dayName;
    private int id;
//  枚举类构造方法必须声明为 private
    private WeekDayEnum(String dayName, int id){
        this.dayName = dayName;
        this.id = id;
    }
    public String getDayName(){
        return this.dayName;
    }
}

WeekDayEnum  enum1 = WeekDayEnum.WEDNESDAY;
        switch(enum1){
            case MONDAY:
            case TUESDAY:
            case WEDNESDAY:
            case THURSDAY:
            case FRIDAY:
                System.out.println("weekday -> "+enum1.getDayName());
                break;
            default:System.out.println("weekend");
        }

利用 name() 方法获取常量定义的字符串 而不要使用 toString 方法由于 toString 能够被复写

JAVA 注解全接触java

注解准备知识:看到一个注解的源码

@Target 使用注解时的做用的目标

ElementType.ANNOTATION_TYPE 能够【给一个注解进行注解】
ElementType.CONSTRUCTOR     能够给构造方法进行注解
ElementType.FIELD           能够给属性进行注解
ElementType.LOCAL_VARIABLE  能够给局部变量进行注解
ElementType.METHOD          能够给方法进行注解
ElementType.PACKAGE         能够给一个包进行注解
ElementType.PARAMETER       能够给一个方法内的参数进行注解
ElementType.TYPE            能够给一个类型进行注解,好比类、接口、枚举

@Retention 注解生效的做用域

RetentionPolicy.SOURCE  注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 
RetentionPolicy.CLASS   注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。 
RetentionPolicy.RUNTIME 注解能够保留到程序运行的时候,它会被加载进入到 JVM 中,因此在程序运行时能够获取到它们。

@Documented 这个元注解确定是和文档有关。它的做用是可以将注解中的元素包含到 Javadoc 中去

@Inherited 代表该注解若是注解了父类,那么子类就会继承这些注解(即便子类没有被任何注解)

@Repeatable 代表该注解能够在同一目标上屡次使用,就像能够贴多个标签

@AliasFor 注解属性的别名 (给注解的这个属性赋值就等于给AliasFor的这个属性赋值)

注解能够有多个属性,没有指明key的,会【默认给注解属性的value()赋值】

自定义注解

Table 注解python

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Table {
    String value();
}

Column 注解android

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Column {
    String value();
}

Filter 类里 使用注解web

package com.ghc.hbase.api;

@Table("user")
public class Filter {
    @Column("id")
    private int id;

    @Column("userName")
    private String userName;

    @Column("nickName")
    private String nickName;

    @Column("age")
    private int age;

    @Column("city")
    private String city;

    @Column("email")
    private  String email;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

实现 反射 orm

package com.ghc.hbase.api;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {

    public static void main(String [] args) throws  Exception{
        Filter filter1 = new Filter();
        filter1.setAge(18);
        filter1.setCity("ny");
        filter1.setEmail("test@gmail.com");
        filter1.setId(666);
        filter1.setUserName("Frank");
        filter1.setNickName("Rocket");
        System.out.println(query(filter1));
    }

    public static String query(Filter filter) throws Exception{
        Class clazz = filter.getClass();
        Table table = (Table)clazz.getAnnotation(Table.class);
        String tableName = table.value();
        StringBuilder sb = new StringBuilder();
        sb.append("select * from ")
                .append(tableName)
                .append(" where  1=1 ");
        Field [] fields = clazz.getDeclaredFields();
        Method [] methods = clazz.getDeclaredMethods();
        for(Field field:fields){
            Object obj = null;
            Column column = (Column)field.getAnnotation(Column.class);
            for(Method method:methods){
//                System.out.println(field.getName() + "---> "+ method.getName());
                String fieldName = field.getName();
                if(("get"+ fieldName).equalsIgnoreCase(method.getName())){
                    obj = method.invoke(filter,null) ;
                    String value = obj instanceof String?"\""+obj+"\"":String.valueOf(obj);
                    sb.append(" and "+column.value()+"="+value);
                }
            }
        }
        return sb.toString();
    }

}

java concurrence 开始, 但不是结束

package com.ghc.concurrence.ConcurrentPractice;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;


public class ThreadPoolPractice {
    private static int threadTotal = 200;
    private static int clientTotal = 5000;
    private static int count = 0;
    public static void main(String [] args){
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        for(int i=0;i<clientTotal;i++){
            executorService.execute(()->{
                try{
                    semaphore.acquire();
                    add();
                    semaphore.release();
                }catch(Exception e){
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
        System.out.println(count);
        
    }
    
    private static void add(){
        count++;
    }

}


package com.ghc.concurrence.ConcurrentPractice;

import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.HashMap;
public class MapExample {
    private static Map<Integer, Integer> map = new HashMap<Integer,Integer>();
    private static int threadNum = 200;
    private static int clientNum = 5000;
    
    public static void main(String[] args){
        ExecutorService exec = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadNum);
        for(int index = 0;index < clientNum; index++){
            final int threadNum = index;
            exec.execute(()->{
                try {
                    semaphore.acquire();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                func(threadNum);
                semaphore.release();
            });
        }
        exec.shutdown();
        System.out.println(map.size());
    }
    public static void func(int threadNum){
        map.put(threadNum, threadNum);
    }
}

编码解码

String info = "中文";
        byte[] infoBytes = info.getBytes(StandardCharsets.UTF_8);
        String decodeInfo = new String(infoBytes,StandardCharsets.UTF_8);
        System.out.println("are they equals ?: "+Objects.equals(info,decodeInfo));

类型提高问题

byte a = 127;
        byte b = 127;
        a = a+b; // 编译不经过, + 操做有会提高为 int 须要 强转
        a = (byte) (a+b); 
        a+=b;
        System.out.println(a) ; // -2

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,能够不设置,当设置成trace时,你会看到log4j2内部各类详细输出-->
<!--monitorInterval:Log4j可以自动检测修改配置 文件和从新配置自己,设置间隔秒数-->
<configuration status="WARN" monitorInterval="30">
    <!--先定义全部的appender-->
    <appenders>
        <!--这个输出控制台的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
        </console>
        <!--文件会打印出全部信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
        <File name="log" fileName="log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>
        <!-- 这个会打印出全部的info及如下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份创建的文件夹下面并进行压缩,做为存档-->
        <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <!--控制台只输出level及以上级别的信息(onMatch),其余的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
        <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <!--而后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>
</configuration>
23)Java 中 ++ 操做符是线程安全的吗?

不是线程安全的操做。它涉及到多个指令,如读取变量值,增长,而后存储回内存,这个过程可能会出现多个线程交差。
clone  是 Object 类的 本地方法

        Number number = new Float(123);
        Integer integer = (Integer) number;
//        Exception in thread "main" java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.Integer

咱们能在 Switch 中使用 String 吗?

从 Java 7 开始,咱们能够在 switch case 中使用字符串,但这仅仅是一个语法糖。内部实如今 switch 中使用字符串的 hash code。

32)Serial 与 Parallel GC之间的不一样之处?

Serial 与 Parallel 在GC执行的时候都会引发 stop-the-world。它们之间主要不一样 serial 收集器是默认的复制收集器,执行 GC 的时候只有一个线程,而 parallel 收集器使用多个 GC 线程来执行。


38)32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?

理论上说上 32 位的 JVM 堆内存能够到达 2^32,即 4GB,但实际上会比这个小不少。不一样操做系统之间不一样,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB。64 位 JVM容许指定最大的堆内存,理论上能够达到 2^64,这是一个很是大的数字,实际上你能够指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。

Runtime runtime = Runtime.getRuntime();
        Long freeMemory = runtime.freeMemory();
        Long totalMemory = runtime.totalMemory();
        Long maxMemory = runtime.maxMemory();
        System.out.println("freeMemory: "+freeMemory/(1024*1024)+"MB");
        System.out.println("totalMemory: "+totalMemory/(1024*1024)+"MB");
        System.out.println("maxMemory: "+maxMemory/(1024*1024)+"MB");
       /* freeMemory: 240MB
        totalMemory: 245MB
        maxMemory: 3625MB*/

41)你能保证 GC 执行吗?

不能,虽然你能够调用 System.gc() 或者 Runtime.getRuntime().gc(),可是没有办法保证 GC 的执行。

42)怎么获取 Java 程序使用的内存?堆使用的百分比?

能够经过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。经过这些方法你也能够获取到堆使用的百分比及堆内存的剩余空间。Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory() 方法总内存的字节数,Runtime.maxMemory() 返回最大内存的字节数。

43)Java 中堆和栈有什么区别?

JVM 中堆和栈属于不一样的内存区域,使用目的也不一样。栈经常使用于保存方法帧和局部变量,而对象老是在堆上分配。栈一般都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的全部线程共享。 Difference between stack and heap memory in Java

finally 中 不建议 抛出异常,由于 finally 中若是有异常出现,那么 finally 老是优先于 try 代码块中的 代码执行 ,这时候 try 中的 异常 若是没有作 特殊处理是不会被 捕获到的,由于 
finally 先抛了一个异常,那么 咱们就极可能丢失异常, 这时候 能够在 finally 外部定义一个 Super。。。。 addSusp。。。, 但仍然不建议这么作。


47)Java 中的编译期常量是什么?使用它又什么风险?

公共静态不可变(public static final )变量也就是咱们所说的编译期常量,这里的 public 可选的。实际上这些变量在编译时会被替换掉,由于编译器知道这些变量的值,而且知道这些变量在运行时不能改变。这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,可是这个值后面被其余人改变了,可是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar。为了不这种状况,当你在更新依赖 JAR 文件时,确保从新编译你的程序。

// 打印 数组  Arrays.toString(arr)  or  Arrays.deepToString(arr) 
Person p1 = new Person("a","f",18);
        Person p2 = new Person("b","m",19);
        Person p3 = new Person("c","f",20);
        Person [] p = new Person[]{p1,p2,p3};
        System.out.println(Arrays.toString(p));


Java 中的 TreeMap 是使用红黑树实现的。

HashTable  线程安全不能够用 null 作key , 而 HashMap 没有同步策略能够用 null 作 key ,一样须要 覆写 equals  hashCode 方法

HashMap<String, Person> personHashMap = new HashMap<>();
        personHashMap.put(null,new Person("a","f",18));
        personHashMap.put(null,new Person("b","m",20));
        System.out.println(personHashMap); // {null=b|20|m}

List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("a","f",18));
        personList.add(new Person("b","m",18));
        personList.add(new Person("c","f",18));

        Iterator<Person> it = personList.iterator();
        while(it.hasNext()){
            Person p = it.next();
            if("c".equals(p.getName())){
                System.out.println("start to remove "+p.getName());
                personList.remove(p); // 这是错误的 会出现 Exception in thread "main" java.util.ConcurrentModificationException
                it.remove(); //  这是对的
            }
        }
        System.out.println(personList);


59)咱们能本身写一个容器类,而后使用 for-each 循环吗?

能够,你能够写一个本身的容器类。若是你想使用 Java 中加强的循环来遍历,你只须要实现 Iterable 接口。若是你实现 Collection 接口,默认就具备该属性

这一点 就跟 python 中 实现 迭代器协议 __iter__ 一个道理

61)有没有可能两个不相等的对象有有相同的 hashcode?

有可能,两个不相等的对象可能会有相同的 hashcode 值,这就是为何在 hashmap 中会有冲突。相等 hashcode 值的规定只是说若是两个对象相等,必须有相同的hashcode 值,可是没有关于不相等对象的任何规定。

因此咱们在 覆写 equals 方法的 同时要 覆写 hashCode 方法,减小 hash 碰撞

65)为何在重写 equals 方法的时候须要重写 hashCode 方法?(答案)

由于有强制的规范指定须要同时重写 hashcode 与 equal 是方法,许多容器类,如 HashMap、HashSet 都依赖于 hashcode 与 equals 的规定

 public static void main(String [] args) {
        test();

    }

    public static void add(Byte b)
    {
        b = b++;
    }
    public static void test()
    {
        Byte a = 127;
        Byte b = 127;
        add(++a);
        System.out.print(a + " ");
        add(b);
        System.out.print(b + "");

    }
//    -128 127

一道算法题

public static void main(String [] args) {
        /*假设有一个数组 A ,int[] A = { 1 , 3 , -1 ,0 , 2 , 1 , -4 , 2 , 0 ,1 ...  N};
         原来是须要查出大于0的数组,可是因为传参错误或者其余缘由,致使查出0和负数了,
         如今要求在不使用新数组和新集合的状况下(即只使用这个A数组,因数组数据比较大,
         且只能用一次循环) 实现正数放到数组的前面,小于等于0的数放到数组的末尾*/
        int[] arr = { 5,6,7,8,9,10,11,-1,-2,-3,-4};
        int base = 0;
        int midIndex = arr.length/2;
        int midValue = arr[midIndex];
        arr[midIndex] = base; // 先将 最中间位置 换位 0
        int start = 0, end = arr.length-1;
        while(start<end){
            System.out.println("start: "+start+" | end: "+end);
            System.out.println(Arrays.toString(arr));
            if(arr[start] < base) {start++;
                if(arr[end] < base){
                    swap(arr,midIndex,end);
                }
                end--;
            }
            else if(arr[start]>base){
                swap(arr,start,midIndex);
                if(arr[end] < base){
                    swap(arr,start,end);
                }
                start++;
                end--;
            }else{
                break;
            }
        }
        System.out.println("********");
        System.out.println(Arrays.toString(arr));
        if(arr[0] == base){
            swap(arr,0,end+1);
        }
        if(arr[arr.length-1] == base){
            swap(arr,start-1,arr.length-1);
        }
        // 将中间那个值还原回去
        arr[start-1] = midValue;
        System.out.println(Arrays.toString(arr));
    }

线程

package com.ghc.test;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) throws Exception {
        List<Thread> threads = new ArrayList<>();
        for (String name : Arrays.asList("Bob", "Alice", "Tom")) {
            threads.add(new HelloThread(name));
        }
        System.out.println("START");
        for (Thread t : threads) {
            t.start();
            //t.join(); //在这里 join 的话,会等待一个 线程完成才会开启下一个 线程
        }
        for(Thread t: threads){
            t.join();
        }
        System.out.println("END");
    }
}

class HelloThread extends Thread {
    String name;
    public HelloThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println("Hello, " + name + "!");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.println("Goodbye, " + name + "!");
    }
}

多线程共享数据安全 -> 屡次运行后发现一个线程副本修改的数据还将来得及写入主存就被另外一个线程读取,就会产生共享数据最后不为 0 的状况

main--> start...
Thread-0--> running...
Thread-1--> running...
main--> end...
ThreadSafe.COUNTER: 10

package com.ghc.test;

import java.util.ArrayList;
import java.util.List;

/**
 * @author :Frank Li
 * @date :Created in 2019/7/9 14:32
 * @description:${description}
 * @modified By:
 * @version: $version$
 */
public class ThreadSafe {
    public static int COUNTER = 0;
    public static final int LOOP = 10000;

    public static void main(String [] args) throws Exception{
        System.out.println(Thread.currentThread().getName()+"--> start...");
        Thread t1 = new Thread(new AddThread());
        Thread t2 = new SubstractThread();
        List<Thread> threadList = new ArrayList<>();
        threadList.add(t1);
        threadList.add(t2);
        for(Thread t:threadList){
            t.start();
        }
        for(Thread t:threadList){
            t.join();
        }
        System.out.println(Thread.currentThread().getName()+"--> end...");
        System.out.println("ThreadSafe.COUNTER: "+ThreadSafe.COUNTER);
    }
}

class AddThread implements Runnable{

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"--> running...");
        for(int i=0;i<ThreadSafe.LOOP;i++){
                ThreadSafe.COUNTER += 1; // 非原子性操做
        }
    }
}

class SubstractThread extends Thread{
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"--> running...");
        for(int i=0;i<ThreadSafe.LOOP;i++){
            ThreadSafe.COUNTER -= 1; // 非原子性操做
        }
    }
}

对于非原子性操做须要加同步锁

package com.ghc.test;

import java.util.ArrayList;
import java.util.List;

/**
 * @author :Frank Li
 * @date :Created in 2019/7/9 14:32
 * @description:${description}
 * @modified By:
 * @version: $version$
 */
public class ThreadSafe {
    public static int COUNTER = 0;
    public static final int LOOP = 10000;

    public static void main(String [] args) throws Exception{
        System.out.println(Thread.currentThread().getName()+"--> start...");
        Thread t1 = new Thread(new AddThread());
        Thread t2 = new SubstractThread();
        List<Thread> threadList = new ArrayList<>();
        threadList.add(t1);
        threadList.add(t2);
        for(Thread t:threadList){
            t.start();
        }
        for(Thread t:threadList){
            t.join();
        }
        System.out.println(Thread.currentThread().getName()+"--> end...");
        System.out.println("ThreadSafe.COUNTER: "+ThreadSafe.COUNTER);
    }
}

class AddThread implements Runnable{

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"--> running...");
        for(int i=0;i<ThreadSafe.LOOP;i++){
                synchronized (Main.class) {
                    ThreadSafe.COUNTER += 1; // 非原子性操做 包括 ILOAD  IADD  ISTORE 三个操做
                }
        }
    }
}

class SubstractThread extends Thread{
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"--> running...");
        for(int i=0;i<ThreadSafe.LOOP;i++){
            synchronized (Main.class) {
                ThreadSafe.COUNTER -= 1; // 非原子性操做 包括 ILOAD  IADD  ISTORE 三个操做
            }
        }
    }
}

原子操做

可重入锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private final Lock lock = new ReentrantLock();
    private int value;
    public void inc(int m){
        if(lock.tryLock()){
            // 若是获取到了锁才进入
            try{
                this.value+=m;
            }finally{
                lock.unlock();
            }
        }
    }
    public void dec(int m){
        if(lock.tryLock()){
            try{
                this.value-=m;
            }finally {
                lock.unlock();
            }
        }
    }
}

别人的

java-study

java面试知识点总结(持续更新,欢迎star,fork补充)面试

抽象类和接口的比较

相同点:
都不能被实例化
都包含抽象方法,这些抽象方法用于描述系统能提供哪些服务,而这些服务是由子类来提供实现的
在系统设计上,二者都表明系统的抽象层,当一个系统使用一棵继承树上的类时,应该尽可能把引用变量声明为继承树的上层抽象类型,这样能够提升两个系统之间的松耦合
不一样点:
在抽象类中能够为部分方法提供默认的实现,从而避免在子类中重复实现它们;可是抽象类不支持多继承。接口不能提供任何方法的实现,可是支持多继承。
接口表明了接口定义者和接口实现者的一种契约;而抽象类和具体类通常而言是一种继承的关系,即二者在概念本质上是不一样。算法

内部类

1.内部类能够很好的实现隐藏,通常的非内部类,是不容许有 private 与protected权限的,但内部类能够
2.内部类拥有外围类的全部元素的访问权限
3.可实现多重继承spring

静态内部类

静态内部类,定义在类中,任何方法外,用static定义;静态内部类只能访问外部类的静态成员。
生成(new)一个静态内部类不须要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象能够直接生成:Outer.Inner in=new Outer.Inner();而不须要经过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。能够定义私有静态内部类。数据库

静态内部类与非静态的内部类区别:

是否能够建立静态的成员方法与成员变量(静态内部类能够建立静态的成员而非静态的内部类不能够)、对于访问外部类的成员的限制(静态内部类只能够访问外部类中的静态成员变量与成员方法而非静态的内部类便可以访问静态的也能够访问非静态的外部类成员方法与成员变量)。这两个差别是静态内部类与非静态外部类最大的差别,也是静态内部类之因此存在的缘由apache

子类为何不能重写父类的静态方法

重写"只能适用于实例方法.不能用于静态方法.对于静态方法,只能隐藏(形式上被重写了,可是不符合的多态的特性),“重写”是用来实现多态性的,只有实例方法是能够实现多态,而静态方法没法实现多态

自动装箱(autoboxing)与拆箱(unboxing)

自动装箱是 Java 编译器在基本数据类型和对应的对象包装类型之间作的一个转化。
好比:把 int 转化成 Integer,double 转化成 Double等,反之就是自动拆箱。
Integer a=1;//这就是一个自动装箱,若是没有自动装箱的话,须要这样Integer a=new Integer(1)
int b=a;//这就是一个自动拆箱,若是没有自动拆箱的话,须要这样:int b=a.intValue()
这样就能看出自动装箱和自动拆箱是简化了基本数据类型和相对应对象的转化步骤
Java中的自动装箱与拆箱

Java中为何要为基本类型提供封装类呢?

一是为了在各类类型间转化,经过各类方法的调用。不然你没法直接经过变量转化。
好比,如今int要转为String
int a=0;
String result=Integer.toString(a);
二是好比我如今要用泛型
List nums;
这里<>须要类。若是你用int。它会报错的

Java 建立对象的几种方式

(1) 用 new 语句建立对象,这是最多见的建立对象的方法

(2) 运用反射手段,调用 java.lang.Class 或者 java.lang.reflect.Constructor 类的 newInstance() 实例方法

(3) 调用对象的 clone() 方法

(4) 运用反序列化手段,调用 java.io.ObjectInputStream 对象的 readObject() 方法

(1)和(2)都会明确的显式的调用构造函数;(3)是在内存上对已有对象的影印,因此不会调用构造函数 (4)是从文件中还原类的对象,也不会调用构造函数。

序列化(Serializable )与反序列化(Deserialize)

对象序列化(Serializable)是指将对象转换为字节序列的过程,而反序列化则是根据字节序列恢复对象的过程。
序列化通常用于如下场景:
1.永久性保存对象,保存对象的字节序列到本地文件中;
2.经过序列化对象在网络中传递对象;
3.经过序列化在进程间传递对象。

只有实现了Serializable和Externalizable接口的类的对象才能被序列化,
java.io.ObjectOutputStream表明对象输出流,它的writeObject(Objectobj)方法可对参数指定的obj对象进行序列化,把获得的字节序列写到一个目标输出流中。
java.io.ObjectInputStream表明对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

覆盖 (Override) 和重载 (Overload)

Java 中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同可是参数不一样的状况;
方法覆盖是说子类从新定义了父类的方法,方法覆盖必须有相同的方法名,参数列表和返回类型。

内存中的栈(stack)、堆(heap)和静态存储区的用法

一般咱们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而经过new关键字和构造器建立的对象放在堆空间;程序中的字面量(literal)如直接书写的100、“hello”和常量都是放在静态存储区中。栈空间操做最快可是也很小,一般大量的对象都是放在堆空间,整个内存包括硬盘上的虚拟内存均可以被当成堆空间来使用。

String str = new String(“hello”);

上面的语句中 str 放在栈上,用 new 建立出来的字符串对象放在堆上,而“hello”这个字面量放在静态存储区。

强引用、弱引用、软引用、虚引用

强引用:如“Object obj = new Object()”,这类引用是 Java 程序中最广泛的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用:它用来描述一些可能还有用,但并不是必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2 以后提供了 SoftReference 类来实现软引用。
弱引用:它也是用来描述非需对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存岛下一次垃圾收集发生以前。当垃圾收集器工做时,不管当前内存是否足够,都会回收掉只被弱引用关联的对象。在 JDK1.2 以后,提供了 WeakReference 类来实现弱引用。
虚引用:最弱的一种引用关系,彻底不会对其生存时间构成影响,也没法经过虚引用来取得一个对象实例。为一个对象设置虚引用关联的惟一目的是但愿能在这个对象被收集器回收时收到一个系统通知。JDK1.2 以后提供了 PhantomReference 类来实现虚引用。
Java 7之基础 - 强引用、弱引用、软引用、虚引用
Java垃圾回收机制与引用类型
内存管理与垃圾回收

Java垃圾回收机制

在C++中,对象所占的内存在程序结束运行以前一直被占用,在明确释放以前不能分配给其它对象;而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块。垃圾收集意味着程序再也不须要的对象是"无用信息",这些信息将被丢弃。当一个对象再也不被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。事实上,除了释放没用的对象,垃圾收集也能够清除内存记录碎片。因为建立对象和垃圾收集器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。
垃圾收集能自动释放内存空间,减轻编程的负担。这使Java虚拟机具备一些优势。首先,它能使编程效率提升。在没有垃圾收集机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候,靠垃圾收集机制可大大缩短期。其次是它保护程序的完整性, 垃圾收集是Java语言安全性策略的一个重要部份。垃圾收集的一个潜在的缺点是它的开销影响程序性能。Java虚拟机必须追踪运行程序中有用的对象,并且最终释放没用的对象。这一个过程须要花费处理器的时间。其次垃圾收集算法的不完备性,早先采用的某些垃圾收集算法就不能保证100%收集到全部的废弃内存。固然随着垃圾收集算法的不断改进以及软硬件运行效率的不断提高,这些问题均可以迎刃而解。
通常来讲,Java开发人员能够不重视JVM中堆内存的分配和垃圾处理收集,可是,充分理解Java的这一特性可让咱们更有效地利用资源。同时要注意finalize()方法是Java的缺省机制,有时为确保对象资源的明确释放,能够编写本身的finalize方法。(引用自百度)
Java 垃圾收集机制

List,Map,Set

图示
由Collection接口派生的两个接口是List和Set;
Vector很是相似ArrayList,可是Vector是同步的;
Stack继承自Vector,实现一个后进先出的堆栈,push和pop,还有peek方法获得栈顶的元素
Set是一种不包含重复的元素的Collection
Map没有继承Collection接口,Map提供key到value的映射,一个Map中不能包含相同的key,每一个key只能映射一个 value
集合你们族

集合类之Vector和ArrayList

1,vector是线程同步的,因此它也是线程安全的,而arraylist是线程异步的,是不安全的。若是不考虑到线程的安全因素,通常用arraylist效率比较高。
2,若是集合中的元素的数目大于目前集合数组的长度时,vector增加率为目前数组长度的100%,而arraylist增加率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有必定的优点。
3,若是查找一个指定位置的数据,vector和arraylist使用的时间是相同的,都是0(1),这个时候使用vector和arraylist均可以

集合类之Hashtable

添加数据使用put(key, value),取出数据使用get(key),这两个基本操做的时间开销为常数。
Hashtable经过initial capacity和load factor两个参数调整性能。一般缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor能够节省空间但相应的查找时间将增大,这会影响像get和put这样的操做。

集合类之HashMap和Hashtable

HashMap Hashtable区别HashMap是Hashtable的轻量级实现(非线程安全的实现),效率上可能高于Hashtable。他们都完成了Map接口。HashMap容许null值做为key和value,而Hashtable不能够。
最大的不一样是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不须要本身为它的方法实现同步,而HashMap 就必须为之提供外同步(Collections.synchronizedMap)。
迭代HashMap采用快速失败机制(不是迭代完成后才告诉你出错了),而Hashtable不是。迭代器的快速失败机制会抛出一个并发修改异常 (ConcurrentModificationException) ,应该仅用于检测程序错误。

HashMap之快速失败机制

咱们知道java.util.HashMap不是线程安全的,所以若是在使用迭代器的过程当中有其余线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是经过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增长这个值,那么在迭代器初始化过程当中会将这个值赋给迭代器的expectedModCount。在迭代过程当中,判断modCount跟expectedModCount是否相等,若是不相等就表示已经有其余线程修改了Map。modCount声明为volatile,保证线程之间修改的可见性。

hashcode的做用

Java中的hashCode方法就是根据必定的规则将与对象相关的信息(好比对象的存储地址,对象的字段等)映射成一个数值,这个数值称做为散列值。
若是集合中已经存在一万条数据或者更多的数据,若是采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的做用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,获得对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,若是table中没有该hashcode值,它就能够直接存进去,不用再进行任何比较了;若是存在该hashcode值,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,因此这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大下降了。
hashcode方法的做用

HashCode和equal方法

一、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中肯定对象的存储地址的;
二、若是两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode必定要相同;
三、若是对象的equals方法被重写,那么对象的hashCode也尽可能重写,而且产生hashCode使用的对象,必定要和equals方法中使用的一致,不然就会违反上面提到的第2点;
四、两个对象的hashCode相同,并不必定表示两个对象就相同,也就是不必定适用于equals(java.lang.Object)方法,只可以说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
HashCode和equal方法

用户线程(User Thread)与守护线程(Daemon Thread)

守护线程,是指用户程序在运行的时候后台提供的一种通用服务的线程。只要当前JVM实例中尚存在任何一个用户线程没有结束,守护线程就所有工做;只有当最后一个用户线程结束时,守护线程随着 JVM 一同结束工做。 守护线程最典型的应用就是 GC (垃圾回收器)。
JAVA并发编程——守护线程(Daemon Thread)

进程和线程的区别

一个进程对应一个程序的执行,而一个线程则是进程执行过程当中的一个单独的执行序列,一个进程能够包含多个线程。线程有时候也被称为轻量级进程。
一个Java虚拟机的实例运行在一个单独的进程中,不一样的线程共享Java虚拟机进程所属的堆内存。这也是为何不一样的线程能够访问同一个对象。线程彼此共享堆内存并保有他们本身独自的栈空间。这也是为何当一个线程调用一个方法时,他的局部变量能够保证线程安全。但堆内存并非线程安全的,必须经过显示的声明同步来确保线程安全。

排序算法

package com.algorithms;

import java.util.Arrays;

/**
 * @author :Frank Li
 * @date :Created in 2019/10/21 9:35
 * @description:${description}
 * @modified By:
 * @version: $version$
 */
public class Sorter {

    public static void main(String[] args) {
        int [] arr = {10,9,8,7,6,5,4,3,2,1};
        System.out.println(Arrays.toString(arr)+"\\n");
        quickSort(arr, 0, arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    public static void quickSort(int [] arr, int leftBound, int rightBound){
        if(leftBound>=rightBound){return;}
        int mid = partition(arr, leftBound, rightBound);
        quickSort(arr, leftBound, mid-1);
        quickSort(arr, mid+1, rightBound);

    }

    public static int partition(int[] arr, int leftBound, int rightBound){
        int left = leftBound;
        int base = rightBound - 1;
        int right = rightBound;

        while(left < right){
            while(arr[left] < arr[base]) {left++;}
            while(arr[right] > arr[base]) {right--;}
            if(left<right){swap(arr, left, right);}
        }

        // 换比较的 基准值
        if(arr[left]<arr[base]) {swap(arr, left, base);}

        return left;
    }

    public static void swap(int[] arr, int left, int right){
        int tmp = arr[left];
        arr[left] = arr[right];
        arr[right] = tmp;
    }

}

interview

2017届春招秋招公司的面试题目

阿里内推(Android开发)

笔试

选择题:

快速排序
二叉树遍历
UML类图

问答题:

1.Java中final,finally,finalize的区别
2.hashmap的特性是什么,和hashtable的区别
3.java线程中sleep和wait方法区别
4.谈谈经常使用容器类的原理和应用场景

面试

1.一个文件中有100万个整数,由空格分开,在程序中判断用户输入的整数是否在此文件中。说出最优的方法

金山一面(Android开发)

1.arraylist与vector的区别
2.优化view
3.线程安全
4.实现树
5.内存限制
6.枚举
7.安卓性能优化工具

阿里面试(JAVA研发)

一面

1.写出知道的全部单例模式的写法
2.整数的二进制找出有几个1,效率要求最高
3.会场安排问题
4.如何用两个栈模拟一个队列
(前面四道考了快40分钟了)
5.TCP三次握手的过程
6.http了解吗?(这道我直接说没有深刻研究web开发)
7.java集合类,哪些线程安全,哪些线程不安全
8.线程安全问题(如何保证线程安全,哪些地方要使用线程安全;lock和synchronized区别,用哪一个更好,为何?)
9.用过volatile吗,说一下?
10.有看过JDK源码吗?(这个我说没有深刻了解过)
11.数据库隔离级别哪几种?(这道直接说忘了,能够提示一下吗)
12.其余问题忘了
(没问项目,全问基础题,算法题最重要,答很差估计进不了)

二面

1.详细问了项目的整个架构和一些具体功能,关键技术的实现?(谈了估计快半个钟)
2.TCP三次握手,以及一些具体问题如”为何要有第三次握手?客户端发给服务器的报文段丢失了怎么办?这时客户端还没收到服务器的确认包,那它会继续发吗?服务器收到以后重传的确认号应该是多少?”等等(两轮都问了TCP,总的来讲就是要精通TCP,滑动窗口,确认重传都要很清楚)
3.线程安全问题(同一面)
4.平衡二叉树(说一下原理?开发实践中哪些地方要用到它?)
5.一个字符串中找出给定字符串,要求效率最高?(字符串匹配的KMP算法)
6.若是HashMap的大小超过了负载因子定义的容量,怎么办?
7.说一下hashcode?(本身关联了equals和==回答)
8.知道Unicode编码吗?(这道我直接说没有深刻研究,怕被问深)
9.其余细节题,如char a=‘海’;float a=1.1; short a=1;分别问了这几个正确吗?
10.想问一道几率论的题目(我直接跟面试官说没修这门课,广外以前把几率论当选修课了…面试官也说那算了)
11.学习理论和作项目你是怎么看的?
12.问了个人职业规划是什么?
13.考不考研,为何?
(能过估计是TCP,线程安全和hashcode与equals这几道题我答的比较好,面试官一直在点头)

hr面

1.看到个人实践经历,问我怎么在学习中能抽出时间作这些实践?
2.看到我以前被内推过安卓,问为何从安卓转作java?(我给了不少缘由,面试官都表示不理解。。)
3.说一下职业规划?怎么学习本身这个方向?
4.详细介绍一个本身的项目?
5.本身扩展过的网络通讯框架有测试过性能吗,比原框架好在哪里?从几个指标来回答?
6.实习去哪一个城市都行吗?
(hr面表现不太好,没有事先准备)

腾讯内推电面(Android开发)

1.一次完整的http访问流程
2.图片内存缓存LRUCache怎么实现
3.App点击桌面图标的启动流程
4.如何进程间通讯?
5.看过Binder源码吗,底层怎么实现
6.Android新版本的特性,如何实现的

CVTE校招(Android开发)

一面

(两个面试官轮流问...)
1.项目介绍,画架构图
2.手写代码实现一个栈
3.TCP握手过程
4.说一下内存溢出
5.遇过内存泄漏吗
6.设计模式,说一下抽象工厂
7.线程模型是怎样的(难倒我了,没复习操做系统...)
8.如何进程间通讯?
9.AIDL如何实现?
10.说一下事件分发机制(这里仍是没理解透)

二面

1.项目介绍,画架构图
2.实习的工做内容
3.手写快速排序
4.一道挺有意思的软件架构设计题,要求设计具备扩展性
5.后面几乎都在自我展示技术了(说了binder机制的内部实现)

三面

1.家庭状况,性格,缺点各类...
2.什么状况下你会拒绝cvte的offer?

滴滴校招(软件研发)

一面(考的很全也真的很难)

1.先作算法题
二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能建立任何新的结点,只能调整树中结点指针的指向。
2.TCP三次握手和四次挥手的过程,timewait,closewait状态分别在哪
3.osi分层
4.进程间通讯方式
5.分页与分段的区别,各自有什么优缺点
6.HashMap、HashTable区别,concurrentHashmap
7.B树
8.java中软引用,虚引用,弱引用区别
9.classLoader
10.gc垃圾回收,分代回收
11.深拷贝 浅拷贝
12.静态内部类,内部类,匿名内部类
13.反射机制
14.sleep()和wait()区别,在同步方法中哪一个能够释放锁?

二面

算法题:
给定文件格式以下
1.1.1.2 1.1.1.200 100
3.2.2.1 3.2.2.144 102
..
每一行表明: 起始IP 结束IP 省份编号
求指定IP的所在省份
int searchProv(char *ip)
可写伪代码 时间10分钟。
(题意都看错,因此被刷了T T)

百度校招(Android开发)

一面

(项目上问了编译器,给一条语句让我画出语法树)
1.说一下final的意义
2.java集合类都问了一遍(基础)
3.如何设计StringBuffer(没答好)
4.死锁的定义,哪4个条件?
5.单例模式
6.手写二分查找,冒泡排序(面试官注重代码细节)
7.activity生命周期,启动模式(问的比较细,singleInstance的activity开启一个新activity,任务栈是怎样状态)
8.handler底层实现?消息队列为空会怎样?(会发生空闲等待,其实就是利用了管道机制)

二面

(面试官也是先问编译器,大公司对这个感兴趣)
1.hashmap的内部实现
2.说一下堆栈的内存结构
3.设计一个栈(不用list,set或map)
4.Asynctask内部实现
5.设计一个线程池?(以前阿里还考过,然而仍是不会...)
6.sleep()和wait()使用过程要注意什么?
7.如何设计一个阻塞队列 ?
8.斐波那契数列的实现(手写递归和非递归)
9.字符串第一个出现一次的字符(遍历一遍,时间o(n),用数组int[256]或hashmap存储)
10.什么是满二叉树,下个定义?(我说了当深度为n时,叶子节点数为2^(n-1)的树...)
11.堆排序,如何初建大根堆?
12.又问了Activity启动模式
13.如何实现AIDL?

三面

1.有一个数列有10万个数,另外一个数列是1亿个数,找出这两个数列共同拥有的数
2.TCP
3.画HTTP报文
4.短链接和长链接
5.两台服务器不一样地方,传输一个文件须要考虑什么因素
6.数据库索引
7.B树,B+树
8.平时怎么学习,学习方法?
9.意向工做地点,工做业务?

携程校招(后台开发)

一面是电话面试,而后通知到公司面试,先写作一份笔试,后面三轮面试

电面

java基础,排序算法之类

现场面

比较难的一道算法题,设计数据结构,手写代码 机票路线,从A地到B地不能直达,要中转1个站,求须要中转1个站的路线占全部路线比例

相关文章
相关标签/搜索